qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v8
@ 2011-07-19 20:41 Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 1/4] qerror: add QERR_JSON_PARSE_ERROR to qerror.c Michael Roth
                   ` (3 more replies)
  0 siblings, 4 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-19 20:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino

This is Set 3/3 of the QAPI+QGA patchsets.

These patches apply on top of qapi-backport-set2-v7, and can also be obtained from:
git://repo.or.cz/qemu/mdroth.git qapi-backport-set3-v8

(Set1+2 are a backport of some of the QAPI-related work from Anthony's
glib tree. The main goal is to get the basic code generation infrastructure in
place so that it can be used by the guest agent to implement a QMP-like guest
interface, and so that future work regarding the QMP conversion to QAPI can be
decoupled from the infrastructure bits. Set3 is the Qemu Guest Agent
(virtagent), rebased on the new code QAPI code generation infrastructure. This
is the first user of QAPI, QMP will follow.)
___

CHANGES SINCE V7:
 - Added checks for errors from fclose()
 - Various fix-ups in schema wording/formatting
 - qemu-ga now builds by default to avoid bitrot

CHANGES SINCE V6:
 - Made "/dev/virtio-ports/org.qemu.guest_agent.0" default device path for agent
 - Consolidated uneeded fcntl() calls into qemu_open()
 - JSON/QMP parse errors now propagated to client
 - Replaced non-assertion uses of g_error() with exit()
 - Added guest-file-flush
 - Removed limit on max read size for guest-file-read
 - 'count' parameters to guest-file-read/guest-file-write are now optional (default to 4KB and size of provided buffer, base64-decoded, respectively)
 - Removed redundant 'file_' and 'shutdown_' prefixes from guest-file-*/guest-shutdown commands, switched to "-" in place of "_" in parameter names, renamed guest-file-read's "buf" param to "buf-b64" and guest-file-write's "data_b64" param to "buf-b64" for consistency.
 - guest-fsfreeze-freeze now returns error objects on error rather as part of it's integer return values, and on error will unfreeze previously frozen filesystems.
 - GUEST_FSFREEZE_STATUS_INPROGRESS removed, GUEST_FSFREEZE_STATUS_ERROR now serves the explicit purpose of noting a failure to find a previously mounted filesytem/directory after initial freeze, or failure to unfreeze 1 or more filesystems.
 - -c/--channel option to qemu-ga is now -m/--method

CHANGES SINCE V5:
 - switched to using qemu malloc/list functions where possible
 - removed unused proxy_path field in struct GAState
 - pid file now opened write-only, removed lockf() in favor of O_EXCL, added SIGINT/SIGTERM signal handlers to handle cleanup
 - cleaned up error-handling, switched to asserts where appropriate, removed unecessary gotos and NULL checks for qemu_free()/qobject_decref()
 - refactored send_payload() using helper functions
 - fixed improper handling of pidfile fd==0
 - changed guest-shutdown's "shutdown_mode" param to "mode"
 - switched to using kernel-generated FDs for guest-file-open rather than an autoincrement value
 - add maximum chunk size of guest-file-read/guest-file-write
 - added checks to avoid guest-file-write from writing data beyond the provided data buffer
 - made logging best-effort, removed handling of failures to log as errors
 - guest-shutdown exec errors now logged to guest syslog, clarified shutdown's asynchronous, no gauruntee nature in schema.

CHANGES SINCE V4:
 - Removed timeout mechanism via worker thread/pthread_cancel due to potential memory leak. Will re-introduce guest-side timeout support in future version.
 - Fixed up fsfreeze code to use enums specified within the guest agent's qapi schema.
 - Fixed memory leak due to a log statement, and added missing cleanup functions for heap-allocated g_error objects.
 - Made "mode" param to guest-file-open optional, defaults to "r" (read-only)

CHANGES SINCE V3:
 - Fixed error-handling issues in fsfreeze commands leading to certain mounted directories causing freeze/thaw operations to fail
 - Added cleanup hook to thaw filesystems on graceful guest agent exit
 - Removed unused enum values and added additional details to schema documentation
 - Fixed build issue that was missed due to deprecated files in source tree, removed unused includes

CHANGES SINCE V2:
 - Rebased on new QAPI code generation framework
 - Dropped ability for QMP to act as a proxy for the guest agent, will be added when new QMP server is backported from Anthony's glib tree
 - Replaced negotiation/control events with a simple 2-way handshake implemented by a standard RPC (guest-sync)
 - Removed enforcement of "pristine" sessions, state is now global/persistant across multiple clients/connections
 - Fixed segfault in logging code
 - Added Jes' filesystem freeze patches 
 - General cleanups

CHANGES SINCE V1:
 - Added guest agent worker thread to execute RPCs in the guest. With this in place we have a reliable timeout mechanism for hung commands, currently set at 30 seconds.
 - Add framework for registering init/cleanup routines for stateful RPCs to clean up after themselves after a timeout.
 - Added the following RPCs: guest-file-{open,close,read,write,seek}, guest-shutdown, guest-info, and removed stubs for guest-view-file (now deprecated)
 - Added GUEST_AGENT_UP/GUEST_AGENT_DOWN QMP events
 - Switched to a TCP-style host-initiated 3-way handshake for channel negotiation, this simplifies client negotiation/interaction over the wire
 - Added configurable log level/log file/pid file options for guest agent
 - Various fixes for bugs/memory leaks and checkpatch.pl fixups

ISSUES/TODOS:
 - Add unit tests for guest agent wire protocol

OVERVIEW

For a better overview of what these patches are meant to accomplish, please reference the RFC for virtagent:

http://comments.gmane.org/gmane.comp.emulators.qemu/96096

These patches integrate the previous virtagent guest agent work directly in QAPI/QMP to leverage it's auto-generated marshalling code. This has numerous benefits:

 - addresses previous concerns over relying on external libraries to handle data encapsulation
 - reduces the need for manual unmarshalling of requests/responses, which makes adding new RPCs much safer/less error-prone, as well as cutting down on redundant code
 - QAPI documentation aligns completely with guest-side RPC implementation
 - is Just Better (TM)

BUILD/USAGE

build:
  ./configure --target-list=x86_64-softmmu
  make qemu-ga #should be built on|for target guest

start guest:
  qemu \
  -drive file=/home/mdroth/vm/rhel6_64_base.raw,snapshot=off,if=virtio \
  -net nic,model=virtio,macaddr=52:54:00:12:34:00 \
  -net tap,script=/etc/qemu-ifup \
  -vnc :1 -m 1024 --enable-kvm \
  -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
  -device virtio-serial \
  -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0"

use guest agent:
  ./qemu-ga -h
  ./qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0

start/use qmp:
  mdroth@illuin:~$ sudo socat unix-connect:/tmp/qga.sock readline
  {"execute":"guest-sync", "arguments":{"id":1234}}
  {"return": 1234}

  {"execute":"guest-ping"}
  {"return": {}}

  {"execute": "guest-info"}
  {"return": {"version": "1.0"}}

  // write "hello world!\n" to /tmp/testqga
  {"execute":"guest-file-open", "arguments":{"path":"/tmp/testqga","mode":"w+"}}
  {"return": 0}
  {"execute":"guest-file-write", "arguments":{"handle":0,"buf-b64":"aGVsbG8gd29ybGQhCg=="}}
  {"return": {"count": 13, "eof": false}}
  {"execute":"guest-file-close", "arguments":{"handle":0}}
  {"return": {}}

  // read back the "hello world!\n" from /tmp/testqga
  {"execute":"guest-file-open", "arguments":{"path":"/tmp/testqga","mode":"r"}}
  {"return": 1}
  {"execute":"guest-file-read", "arguments":{"handle":1,"count":1024}}
  {"return": {"buf-b64": "aGVsbG8gd29ybGQhCg==", "count": 13, "eof": true}}
  {"execute":"guest-file-close","arguments":{"handle":1}}
  {"return": {}}

 Makefile                        |   24 ++-
 configure                       |    2 +
 qapi-schema-guest.json          |  217 +++++++++++++
 qemu-ga.c                       |  671 +++++++++++++++++++++++++++++++++++++++
 qerror.c                        |   13 +
 qerror.h                        |    6 +
 qga/guest-agent-command-state.c |   73 +++++
 qga/guest-agent-commands.c      |  518 ++++++++++++++++++++++++++++++
 qga/guest-agent-core.h          |   31 ++
 9 files changed, 1550 insertions(+), 5 deletions(-)

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

* [Qemu-devel] [PATCH v8 1/4] qerror: add QERR_JSON_PARSE_ERROR to qerror.c
  2011-07-19 20:41 [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v8 Michael Roth
@ 2011-07-19 20:41 ` Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 2/4] guest agent: command state class Michael Roth
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-19 20:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino

Missing from previous addition of error to qerror.h. Needed for
qerror_format() and friends.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qerror.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/qerror.c b/qerror.c
index d7fcd93..c92adfc 100644
--- a/qerror.c
+++ b/qerror.c
@@ -141,6 +141,11 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "Invalid JSON syntax",
     },
     {
+        .error_fmt = QERR_JSON_PARSE_ERROR,
+        .desc      = "JSON parse error, %(message)",
+
+    },
+    {
         .error_fmt = QERR_KVM_MISSING_CAP,
         .desc      = "Using KVM without %(capability), %(feature) unavailable",
     },
-- 
1.7.0.4

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

* [Qemu-devel] [PATCH v8 2/4] guest agent: command state class
  2011-07-19 20:41 [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v8 Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 1/4] qerror: add QERR_JSON_PARSE_ERROR to qerror.c Michael Roth
@ 2011-07-19 20:41 ` Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 4/4] guest agent: add guest agent RPCs/commands Michael Roth
  3 siblings, 0 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-19 20:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino


Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile                        |    4 ++-
 configure                       |    1 +
 qga/guest-agent-command-state.c |   73 +++++++++++++++++++++++++++++++++++++++
 qga/guest-agent-core.h          |   25 +++++++++++++
 4 files changed, 102 insertions(+), 1 deletions(-)
 create mode 100644 qga/guest-agent-command-state.c
 create mode 100644 qga/guest-agent-core.h

diff --git a/Makefile b/Makefile
index d7c8567..b8cdf0e 100644
--- a/Makefile
+++ b/Makefile
@@ -182,6 +182,8 @@ test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $
 test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
 test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
 
+QGALIB=qga/guest-agent-command-state.o
+
 QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
 
 clean:
@@ -190,7 +192,7 @@ clean:
 	rm -f qemu-options.def
 	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
 	rm -Rf .libs
-	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d
+	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
 	rm -f qemu-img-cmds.h
 	rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
 	rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
diff --git a/configure b/configure
index 02c552e..6a03002 100755
--- a/configure
+++ b/configure
@@ -3487,6 +3487,7 @@ DIRS="$DIRS pc-bios/spapr-rtas"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS fsdev ui"
 DIRS="$DIRS qapi"
+DIRS="$DIRS qga"
 FILES="Makefile tests/Makefile"
 FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
 FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
diff --git a/qga/guest-agent-command-state.c b/qga/guest-agent-command-state.c
new file mode 100644
index 0000000..bc6e0bd
--- /dev/null
+++ b/qga/guest-agent-command-state.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU Guest Agent command state interfaces
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Michael Roth      <mdroth@linux.vnet.ibm.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 "qga/guest-agent-core.h"
+
+struct GACommandState {
+    GSList *groups;
+};
+
+typedef struct GACommandGroup {
+    void (*init)(void);
+    void (*cleanup)(void);
+} GACommandGroup;
+
+/* handle init/cleanup for stateful guest commands */
+
+void ga_command_state_add(GACommandState *cs,
+                          void (*init)(void),
+                          void (*cleanup)(void))
+{
+    GACommandGroup *cg = qemu_mallocz(sizeof(GACommandGroup));
+    cg->init = init;
+    cg->cleanup = cleanup;
+    cs->groups = g_slist_append(cs->groups, cg);
+}
+
+static void ga_command_group_init(gpointer opaque, gpointer unused)
+{
+    GACommandGroup *cg = opaque;
+
+    g_assert(cg);
+    if (cg->init) {
+        cg->init();
+    }
+}
+
+void ga_command_state_init_all(GACommandState *cs)
+{
+    g_assert(cs);
+    g_slist_foreach(cs->groups, ga_command_group_init, NULL);
+}
+
+static void ga_command_group_cleanup(gpointer opaque, gpointer unused)
+{
+    GACommandGroup *cg = opaque;
+
+    g_assert(cg);
+    if (cg->cleanup) {
+        cg->cleanup();
+    }
+}
+
+void ga_command_state_cleanup_all(GACommandState *cs)
+{
+    g_assert(cs);
+    g_slist_foreach(cs->groups, ga_command_group_cleanup, NULL);
+}
+
+GACommandState *ga_command_state_new(void)
+{
+    GACommandState *cs = qemu_mallocz(sizeof(GACommandState));
+    cs->groups = NULL;
+    return cs;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
new file mode 100644
index 0000000..688f120
--- /dev/null
+++ b/qga/guest-agent-core.h
@@ -0,0 +1,25 @@
+/*
+ * QEMU Guest Agent core declarations
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Adam Litke        <aglitke@linux.vnet.ibm.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.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 "qapi/qmp-core.h"
+#include "qemu-common.h"
+
+#define QGA_VERSION "1.0"
+
+typedef struct GACommandState GACommandState;
+
+void ga_command_state_add(GACommandState *cs,
+                          void (*init)(void),
+                          void (*cleanup)(void));
+void ga_command_state_init_all(GACommandState *cs);
+void ga_command_state_cleanup_all(GACommandState *cs);
+GACommandState *ga_command_state_new(void);
-- 
1.7.0.4

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

* [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-19 20:41 [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v8 Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 1/4] qerror: add QERR_JSON_PARSE_ERROR to qerror.c Michael Roth
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 2/4] guest agent: command state class Michael Roth
@ 2011-07-19 20:41 ` Michael Roth
  2011-07-20 17:56   ` Luiz Capitulino
                     ` (2 more replies)
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 4/4] guest agent: add guest agent RPCs/commands Michael Roth
  3 siblings, 3 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-19 20:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino

This is the actual guest daemon, it listens for requests over a
virtio-serial/isa-serial/unix socket channel and routes them through
to dispatch routines, and writes the results back to the channel in
a manner similar to QMP.

A shorthand invocation:

  qemu-ga -d

Is equivalent to:

  qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
          -f /var/run/qemu-ga.pid -d

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile               |    8 +-
 configure              |    1 +
 qemu-ga.c              |  667 ++++++++++++++++++++++++++++++++++++++++++++++++
 qga/guest-agent-core.h |    4 +
 4 files changed, 677 insertions(+), 3 deletions(-)
 create mode 100644 qemu-ga.c

diff --git a/Makefile b/Makefile
index b8cdf0e..0d2e33d 100644
--- a/Makefile
+++ b/Makefile
@@ -140,7 +140,7 @@ endif
 ######################################################################
 
 qemu-img.o: qemu-img-cmds.h
-qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
+qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
 
 qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
 
@@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o
 
 QGALIB=qga/guest-agent-command-state.o
 
+qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
+
 QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
 
 clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 	rm -f qemu-options.def
-	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
+	rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
 	rm -Rf .libs
 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
 	rm -f qemu-img-cmds.h
@@ -386,4 +388,4 @@ tarbin:
 	$(mandir)/man8/qemu-nbd.8
 
 # Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
diff --git a/configure b/configure
index 6a03002..445f674 100755
--- a/configure
+++ b/configure
@@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
   tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
   if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
       tools="qemu-nbd\$(EXESUF) $tools"
+      tools="qemu-ga\$(EXESUF) $tools"
     if [ "$check_utests" = "yes" ]; then
       tools="check-qint check-qstring check-qdict check-qlist $tools"
       tools="check-qfloat check-qjson $tools"
diff --git a/qemu-ga.c b/qemu-ga.c
new file mode 100644
index 0000000..eb09100
--- /dev/null
+++ b/qemu-ga.c
@@ -0,0 +1,667 @@
+/*
+ * QEMU Guest Agent
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Adam Litke        <aglitke@linux.vnet.ibm.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.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 <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <getopt.h>
+#include <termios.h>
+#include <syslog.h>
+#include "qemu_socket.h"
+#include "json-streamer.h"
+#include "json-parser.h"
+#include "qint.h"
+#include "qjson.h"
+#include "qga/guest-agent-core.h"
+#include "module.h"
+#include "signal.h"
+#include "qerror.h"
+#include "error_int.h"
+
+#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
+#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
+#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
+#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
+
+struct GAState {
+    JSONMessageParser parser;
+    GMainLoop *main_loop;
+    guint conn_id;
+    GSocket *conn_sock;
+    GIOChannel *conn_channel;
+    guint listen_id;
+    GSocket *listen_sock;
+    GIOChannel *listen_channel;
+    const char *path;
+    const char *method;
+    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
+    GACommandState *command_state;
+    GLogLevelFlags log_level;
+    FILE *log_file;
+    bool logging_enabled;
+};
+
+static struct GAState *ga_state;
+
+static void quit_handler(int sig)
+{
+    g_debug("recieved signal num %d, quitting", sig);
+
+    if (g_main_loop_is_running(ga_state->main_loop)) {
+        g_main_loop_quit(ga_state->main_loop);
+    }
+}
+
+static void register_signal_handlers(void)
+{
+    struct sigaction sigact;
+    int ret;
+
+    memset(&sigact, 0, sizeof(struct sigaction));
+    sigact.sa_handler = quit_handler;
+
+    ret = sigaction(SIGINT, &sigact, NULL);
+    if (ret == -1) {
+        g_error("error configuring signal handler: %s", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    ret = sigaction(SIGTERM, &sigact, NULL);
+    if (ret == -1) {
+        g_error("error configuring signal handler: %s", strerror(errno));
+    }
+}
+
+static void usage(const char *cmd)
+{
+    printf(
+"Usage: %s -c <channel_opts>\n"
+"QEMU Guest Agent %s\n"
+"\n"
+"  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
+"                    isa-serial (virtio-serial is the default)\n"
+"  -p, --path        device/socket path (%s is the default for virtio-serial)\n"
+"  -l, --logfile     set logfile path, logs to stderr by default\n"
+"  -f, --pidfile     specify pidfile (default is %s)\n"
+"  -v, --verbose     log extra debugging information\n"
+"  -V, --version     print version information and exit\n"
+"  -d, --daemonize   become a daemon\n"
+"  -h, --help        display this help and exit\n"
+"\n"
+"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
+    , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
+}
+
+static void conn_channel_close(GAState *s);
+
+static const char *ga_log_level_str(GLogLevelFlags level)
+{
+    switch (level & G_LOG_LEVEL_MASK) {
+        case G_LOG_LEVEL_ERROR:
+            return "error";
+        case G_LOG_LEVEL_CRITICAL:
+            return "critical";
+        case G_LOG_LEVEL_WARNING:
+            return "warning";
+        case G_LOG_LEVEL_MESSAGE:
+            return "message";
+        case G_LOG_LEVEL_INFO:
+            return "info";
+        case G_LOG_LEVEL_DEBUG:
+            return "debug";
+        default:
+            return "user";
+    }
+}
+
+bool ga_logging_enabled(GAState *s)
+{
+    return s->logging_enabled;
+}
+
+void ga_disable_logging(GAState *s)
+{
+    s->logging_enabled = false;
+}
+
+void ga_enable_logging(GAState *s)
+{
+    s->logging_enabled = true;
+}
+
+static void ga_log(const gchar *domain, GLogLevelFlags level,
+                   const gchar *msg, gpointer opaque)
+{
+    GAState *s = opaque;
+    GTimeVal time;
+    const char *level_str = ga_log_level_str(level);
+
+    if (!ga_logging_enabled(s)) {
+        return;
+    }
+
+    level &= G_LOG_LEVEL_MASK;
+    if (g_strcmp0(domain, "syslog") == 0) {
+        syslog(LOG_INFO, "%s: %s", level_str, msg);
+    } else if (level & s->log_level) {
+        g_get_current_time(&time);
+        fprintf(s->log_file,
+                "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
+        fflush(s->log_file);
+    }
+}
+
+static void become_daemon(const char *pidfile)
+{
+    pid_t pid, sid;
+    int pidfd;
+    char *pidstr = NULL;
+
+    pid = fork();
+    if (pid < 0) {
+        exit(EXIT_FAILURE);
+    }
+    if (pid > 0) {
+        exit(EXIT_SUCCESS);
+    }
+
+    pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
+    if (pidfd == -1) {
+        g_critical("Cannot create pid file, %s", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    if (asprintf(&pidstr, "%d", getpid()) == -1) {
+        g_critical("Cannot allocate memory");
+        goto fail;
+    }
+    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+        free(pidstr);
+        g_critical("Failed to write pid file");
+        goto fail;
+    }
+
+    umask(0);
+    sid = setsid();
+    if (sid < 0) {
+        goto fail;
+    }
+    if ((chdir("/")) < 0) {
+        goto fail;
+    }
+
+    close(STDIN_FILENO);
+    close(STDOUT_FILENO);
+    close(STDERR_FILENO);
+    free(pidstr);
+    return;
+
+fail:
+    unlink(pidfile);
+    g_critical("failed to daemonize");
+    exit(EXIT_FAILURE);
+}
+
+static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
+                                 gsize count)
+{
+    GError *err = NULL;
+    gsize written = 0;
+    GIOStatus status;
+
+    while (count) {
+        status = g_io_channel_write_chars(channel, buf, count, &written, &err);
+        g_debug("sending data, count: %d", (int)count);
+        if (err != NULL) {
+            g_warning("error sending newline: %s", err->message);
+            return err->code;
+        }
+        if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
+            return -EPIPE;
+        }
+
+        if (status == G_IO_STATUS_NORMAL) {
+            count -= written;
+        }
+    }
+
+    return 0;
+}
+
+static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
+{
+    int ret = 0;
+    const char *buf;
+    QString *payload_qstr;
+    GError *err = NULL;
+
+    g_assert(payload && channel);
+
+    payload_qstr = qobject_to_json(payload);
+    if (!payload_qstr) {
+        return -EINVAL;
+    }
+
+    qstring_append_chr(payload_qstr, '\n');
+    buf = qstring_get_str(payload_qstr);
+    ret = conn_channel_send_buf(channel, buf, strlen(buf));
+    if (ret) {
+        goto out_free;
+    }
+
+    g_io_channel_flush(channel, &err);
+    if (err != NULL) {
+        g_warning("error flushing payload: %s", err->message);
+        ret = err->code;
+        goto out_free;
+    }
+
+out_free:
+    QDECREF(payload_qstr);
+    if (err) {
+        g_error_free(err);
+    }
+    return ret;
+}
+
+static void process_command(GAState *s, QDict *req)
+{
+    QObject *rsp = NULL;
+    int ret;
+
+    g_assert(req);
+    g_debug("processing command");
+    rsp = qmp_dispatch(QOBJECT(req));
+    if (rsp) {
+        ret = conn_channel_send_payload(s->conn_channel, rsp);
+        if (ret) {
+            g_warning("error sending payload: %s", strerror(ret));
+        }
+        qobject_decref(rsp);
+    } else {
+        g_warning("error getting response");
+    }
+}
+
+/* handle requests/control events coming in over the channel */
+static void process_event(JSONMessageParser *parser, QList *tokens)
+{
+    GAState *s = container_of(parser, GAState, parser);
+    QObject *obj;
+    QDict *qdict;
+    Error *err = NULL;
+    int ret;
+
+    g_assert(s && parser);
+
+    g_debug("process_event: called");
+    obj = json_parser_parse_err(tokens, NULL, &err);
+    if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
+        qobject_decref(obj);
+        qdict = qdict_new();
+        if (!err) {
+            g_warning("failed to parse event: unknown error");
+            error_set(&err, QERR_JSON_PARSING);
+        } else {
+            g_warning("failed to parse event: %s", error_get_pretty(err));
+        }
+        qdict_put_obj(qdict, "error", error_get_qobject(err));
+        error_free(err);
+    } else {
+        qdict = qobject_to_qdict(obj);
+    }
+
+    g_assert(qdict);
+
+    /* handle host->guest commands */
+    if (qdict_haskey(qdict, "execute")) {
+        process_command(s, qdict);
+    } else {
+        if (!qdict_haskey(qdict, "error")) {
+            QDECREF(qdict);
+            qdict = qdict_new();
+            g_warning("unrecognized payload format");
+            error_set(&err, QERR_UNSUPPORTED);
+            qdict_put_obj(qdict, "error", error_get_qobject(err));
+            error_free(err);
+        }
+        ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
+        if (ret) {
+            g_warning("error sending payload: %s", strerror(ret));
+        }
+    }
+
+    QDECREF(qdict);
+}
+
+static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
+                                  gpointer data)
+{
+    GAState *s = data;
+    gchar buf[1024];
+    gsize count;
+    GError *err = NULL;
+    memset(buf, 0, 1024);
+    GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
+                                               &count, &err);
+    if (err != NULL) {
+        g_warning("error reading channel: %s", err->message);
+        conn_channel_close(s);
+        g_error_free(err);
+        return false;
+    }
+    switch (status) {
+    case G_IO_STATUS_ERROR:
+        g_warning("problem");
+        return false;
+    case G_IO_STATUS_NORMAL:
+        g_debug("read data, count: %d, data: %s", (int)count, buf);
+        json_message_parser_feed(&s->parser, (char *)buf, (int)count);
+    case G_IO_STATUS_AGAIN:
+        /* virtio causes us to spin here when no process is attached to
+         * host-side chardev. sleep a bit to mitigate this
+         */
+        if (s->virtio) {
+            usleep(100*1000);
+        }
+        return true;
+    case G_IO_STATUS_EOF:
+        g_debug("received EOF");
+        conn_channel_close(s);
+        if (s->virtio) {
+            return true;
+        }
+        return false;
+    default:
+        g_warning("unknown channel read status, closing");
+        conn_channel_close(s);
+        return false;
+    }
+    return true;
+}
+
+static int conn_channel_add(GAState *s, int fd)
+{
+    GIOChannel *conn_channel;
+    guint conn_id;
+    GError *err = NULL;
+
+    g_assert(s && !s->conn_channel);
+    conn_channel = g_io_channel_unix_new(fd);
+    g_assert(conn_channel);
+    g_io_channel_set_encoding(conn_channel, NULL, &err);
+    if (err != NULL) {
+        g_warning("error setting channel encoding to binary");
+        g_error_free(err);
+        return -1;
+    }
+    conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
+                             conn_channel_read, s);
+    if (err != NULL) {
+        g_warning("error adding io watch: %s", err->message);
+        g_error_free(err);
+        return -1;
+    }
+    s->conn_channel = conn_channel;
+    s->conn_id = conn_id;
+    return 0;
+}
+
+static gboolean listen_channel_accept(GIOChannel *channel,
+                                      GIOCondition condition, gpointer data)
+{
+    GAState *s = data;
+    GError *err = NULL;
+    g_assert(channel != NULL);
+    int ret;
+    bool accepted = false;
+
+    s->conn_sock = g_socket_accept(s->listen_sock, NULL, &err);
+    if (err != NULL) {
+        g_warning("error converting fd to gsocket: %s", err->message);
+        g_error_free(err);
+        goto out;
+    }
+    ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
+    if (ret) {
+        g_warning("error setting up connection");
+        goto out;
+    }
+    accepted = true;
+
+out:
+    /* only accept 1 connection at a time */
+    return !accepted;
+}
+
+/* start polling for readable events on listen fd, new==true
+ * indicates we should use the existing s->listen_channel
+ */
+static int listen_channel_add(GAState *s, int listen_fd, bool new)
+{
+    GError *err = NULL;
+    guint listen_id;
+
+    if (new) {
+        s->listen_channel = g_io_channel_unix_new(listen_fd);
+        if (s->listen_sock) {
+            g_object_unref(s->listen_sock);
+        }
+        s->listen_sock = g_socket_new_from_fd(listen_fd, &err);
+        if (err != NULL) {
+            g_warning("error converting fd to gsocket: %s", err->message);
+            g_error_free(err);
+            return -1;
+        }
+    }
+    listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
+                               listen_channel_accept, s);
+    if (err != NULL) {
+        g_warning("error adding io watch: %s", err->message);
+        g_error_free(err);
+        return -1;
+    }
+    return 0;
+}
+
+/* cleanup state for closed connection/session, start accepting new
+ * connections if we're in listening mode
+ */
+static void conn_channel_close(GAState *s)
+{
+    if (strcmp(s->method, "unix-listen") == 0) {
+        g_io_channel_shutdown(s->conn_channel, true, NULL);
+        g_object_unref(s->conn_sock);
+        s->conn_sock = NULL;
+        listen_channel_add(s, 0, false);
+    } else if (strcmp(s->method, "virtio-serial") == 0) {
+        /* we spin on EOF for virtio-serial, so back off a bit. also,
+         * dont close the connection in this case, it'll resume normal
+         * operation when another process connects to host chardev
+         */
+        usleep(100*1000);
+        goto out_noclose;
+    }
+    g_io_channel_unref(s->conn_channel);
+    s->conn_channel = NULL;
+    s->conn_id = 0;
+out_noclose:
+    return;
+}
+
+static void init_guest_agent(GAState *s)
+{
+    struct termios tio;
+    int ret, fd;
+
+    if (s->method == NULL) {
+        /* try virtio-serial as our default */
+        s->method = "virtio-serial";
+    }
+
+    if (s->path == NULL) {
+        if (strcmp(s->method, "virtio-serial") != 0) {
+            g_critical("must specify a path for this channel");
+            exit(EXIT_FAILURE);
+        }
+        /* try the default path for the virtio-serial port */
+        s->path = QGA_VIRTIO_PATH_DEFAULT;
+    }
+
+    if (strcmp(s->method, "virtio-serial") == 0) {
+        s->virtio = true;
+        fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
+        if (fd == -1) {
+            g_critical("error opening channel: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        ret = conn_channel_add(s, fd);
+        if (ret) {
+            g_critical("error adding channel to main loop");
+            exit(EXIT_FAILURE);
+        }
+    } else if (strcmp(s->method, "isa-serial") == 0) {
+        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
+        if (fd == -1) {
+            g_critical("error opening channel: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        tcgetattr(fd, &tio);
+        /* set up serial port for non-canonical, dumb byte streaming */
+        tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
+                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
+                         IMAXBEL);
+        tio.c_oflag = 0;
+        tio.c_lflag = 0;
+        tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
+        /* 1 available byte min or reads will block (we'll set non-blocking
+         * elsewhere, else we have to deal with read()=0 instead)
+         */
+        tio.c_cc[VMIN] = 1;
+        tio.c_cc[VTIME] = 0;
+        /* flush everything waiting for read/xmit, it's garbage at this point */
+        tcflush(fd, TCIFLUSH);
+        tcsetattr(fd, TCSANOW, &tio);
+        ret = conn_channel_add(s, fd);
+        if (ret) {
+            g_error("error adding channel to main loop");
+        }
+    } else if (strcmp(s->method, "unix-listen") == 0) {
+        fd = unix_listen(s->path, NULL, strlen(s->path));
+        if (fd == -1) {
+            g_critical("error opening path: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        ret = listen_channel_add(s, fd, true);
+        if (ret) {
+            g_critical("error binding/listening to specified socket");
+            exit(EXIT_FAILURE);
+        }
+    } else {
+        g_critical("unsupported channel method/type: %s", s->method);
+        exit(EXIT_FAILURE);
+    }
+
+    json_message_parser_init(&s->parser, process_event);
+    s->main_loop = g_main_loop_new(NULL, false);
+}
+
+int main(int argc, char **argv)
+{
+    const char *sopt = "hVvdm:p:l:f:";
+    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
+    const struct option lopt[] = {
+        { "help", 0, NULL, 'h' },
+        { "version", 0, NULL, 'V' },
+        { "logfile", 0, NULL, 'l' },
+        { "pidfile", 0, NULL, 'f' },
+        { "verbose", 0, NULL, 'v' },
+        { "method", 0, NULL, 'm' },
+        { "path", 0, NULL, 'p' },
+        { "daemonize", 0, NULL, 'd' },
+        { NULL, 0, NULL, 0 }
+    };
+    int opt_ind = 0, ch, daemonize = 0;
+    GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
+    FILE *log_file = stderr;
+    GAState *s;
+
+    while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
+        switch (ch) {
+        case 'm':
+            method = optarg;
+            break;
+        case 'p':
+            path = optarg;
+            break;
+        case 'l':
+            log_file = fopen(optarg, "a");
+            if (!log_file) {
+                g_critical("unable to open specified log file: %s",
+                           strerror(errno));
+                return EXIT_FAILURE;
+            }
+            break;
+        case 'f':
+            pidfile = optarg;
+            break;
+        case 'v':
+            /* enable all log levels */
+            log_level = G_LOG_LEVEL_MASK;
+            break;
+        case 'V':
+            printf("QEMU Guest Agent %s\n", QGA_VERSION);
+            return 0;
+        case 'd':
+            daemonize = 1;
+            break;
+        case 'h':
+            usage(argv[0]);
+            return 0;
+        case '?':
+            g_print("Unknown option, try '%s --help' for more information.\n",
+                    argv[0]);
+            return EXIT_FAILURE;
+        }
+    }
+
+    if (daemonize) {
+        g_debug("starting daemon");
+        become_daemon(pidfile);
+    }
+
+    g_type_init();
+    g_thread_init(NULL);
+
+    s = qemu_mallocz(sizeof(GAState));
+    s->conn_id = 0;
+    s->conn_channel = NULL;
+    s->path = path;
+    s->method = method;
+    s->log_file = log_file;
+    s->log_level = log_level;
+    g_log_set_default_handler(ga_log, s);
+    g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
+    s->logging_enabled = true;
+    ga_state = s;
+
+    module_call_init(MODULE_INIT_QAPI);
+    init_guest_agent(ga_state);
+    register_signal_handlers();
+
+    g_main_loop_run(ga_state->main_loop);
+
+    unlink(pidfile);
+
+    return 0;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index 688f120..66d1729 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -15,6 +15,7 @@
 
 #define QGA_VERSION "1.0"
 
+typedef struct GAState GAState;
 typedef struct GACommandState GACommandState;
 
 void ga_command_state_add(GACommandState *cs,
@@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
 void ga_command_state_init_all(GACommandState *cs);
 void ga_command_state_cleanup_all(GACommandState *cs);
 GACommandState *ga_command_state_new(void);
+bool ga_logging_enabled(GAState *s);
+void ga_disable_logging(GAState *s);
+void ga_enable_logging(GAState *s);
-- 
1.7.0.4

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

* [Qemu-devel] [PATCH v8 4/4] guest agent: add guest agent RPCs/commands
  2011-07-19 20:41 [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v8 Michael Roth
                   ` (2 preceding siblings ...)
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon Michael Roth
@ 2011-07-19 20:41 ` Michael Roth
  3 siblings, 0 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-19 20:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino

This adds the initial set of QMP/QAPI commands provided by the guest
agent:

guest-sync
guest-ping
guest-info
guest-shutdown
guest-file-open
guest-file-read
guest-file-write
guest-file-seek
guest-file-flush
guest-file-close
guest-fsfreeze-freeze
guest-fsfreeze-thaw
guest-fsfreeze-status

The input/output specification for these commands are documented in the
schema.

Example usage:

  host:
    qemu -device virtio-serial \
         -chardev socket,path=/tmp/vs0.sock,server,nowait,id=qga0 \
         -device virtserialport,chardev=qga0,name=org.qemu.quest_agent.0
         ...

    echo "{'execute':'guest-info'}" | socat stdio unix-connect:/tmp/qga0.sock

  guest:
    qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
            -p /var/run/qemu-guest-agent.pid -d

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile                   |   16 +-
 qapi-schema-guest.json     |  217 ++++++++++++++++++
 qemu-ga.c                  |    4 +
 qerror.c                   |    8 +
 qerror.h                   |    6 +
 qga/guest-agent-commands.c |  518 ++++++++++++++++++++++++++++++++++++++++++++
 qga/guest-agent-core.h     |    2 +
 7 files changed, 768 insertions(+), 3 deletions(-)
 create mode 100644 qapi-schema-guest.json
 create mode 100644 qga/guest-agent-commands.c

diff --git a/Makefile b/Makefile
index 0d2e33d..f3a03ad 100644
--- a/Makefile
+++ b/Makefile
@@ -164,7 +164,7 @@ check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjs
 
 $(qapi-obj-y): $(GENERATED_HEADERS)
 qapi-dir := qapi-generated
-test-visitor.o test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir)
+test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
 
 $(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h
 $(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
@@ -176,15 +176,25 @@ $(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c
 $(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
 	    $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, "  GEN   $@")
 
+$(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h
+$(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, "  GEN   $@")
+$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h
+$(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, "  GEN   $@")
+$(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, "  GEN   $@")
+
 test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
 test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
 
 test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
 test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
 
-QGALIB=qga/guest-agent-command-state.o
+QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o
 
-qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
+qemu-ga.o: $(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) $(qapi-obj-y)
+qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o
 
 QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
 
diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
new file mode 100644
index 0000000..fde5971
--- /dev/null
+++ b/qapi-schema-guest.json
@@ -0,0 +1,217 @@
+# *-*- Mode: Python -*-*
+
+##
+# @guest-sync:
+#
+# Echo back a unique integer value
+#
+# 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. All guest agent responses should be
+# ignored until the provided unique integer value is returned,
+# and it is up to the client to handle stale whole or
+# partially-delivered JSON text in such a way that this response
+# can be obtained.
+#
+# Such clients should also preceed this command
+# with a 0xFF byte to make such the guest agent flushes any
+# partially read JSON data from a previous session.
+#
+# @id: randomly generated 64-bit integer
+#
+# Returns: The unique integer id passed in by the client
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-sync'
+  'data':    { 'id': 'int' },
+  'returns': 'int' }
+
+##
+# @guest-ping:
+#
+# Ping the guest agent, a non-error return implies success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-ping' }
+
+##
+# @guest-info:
+#
+# Get some information about the guest agent.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} }
+{ 'command': 'guest-info',
+  'returns': 'GuestAgentInfo' }
+
+##
+# @guest-shutdown:
+#
+# Initiate guest-activated shutdown. Note: this is an asynchronous
+# shutdown request, with no guaruntee of successful shutdown. Errors
+# will be logged to guest's syslog.
+#
+# @mode: #optional "halt", "powerdown" (default), or "reboot"
+#
+# Returns: Nothing on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } }
+
+##
+# @guest-file-open:
+#
+# Open a file in the guest and retrieve a file handle for it
+#
+# @filepath: Full path to the file in the guest to open.
+#
+# @mode: #optional open mode, as per fopen(), "r" is the default.
+#
+# Returns: Guest file handle on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-open',
+  'data':    { 'path': 'str', '*mode': 'str' },
+  'returns': 'int' }
+
+##
+# @guest-file-close:
+#
+# Close an open file in the guest
+#
+# @handle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-close',
+  'data': { 'handle': 'int' } }
+
+##
+# @guest-file-read:
+#
+# Read from an open file in the guest. Data will be base64-encoded
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @count: #optional maximum number of bytes to read (default is 4KB)
+#
+# Returns: GuestFileRead on success. Note: count is number of bytes read
+#          *before* base64 encoding bytes read.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileRead',
+  'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-read',
+  'data':    { 'handle': 'int', '*count': 'int' },
+  'returns': 'GuestFileRead' }
+
+##
+# @guest-file-write:
+#
+# Write to an open file in the guest.
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @buf-b64: base64-encoded string representing data to be written
+#
+# @count: #optional bytes to write (actual bytes, after base64-decode),
+#         default is all content in buf-b64 buffer after base64 decoding
+#
+# Returns: GuestFileWrite on success. Note: count is the number of bytes
+#          base64-decoded bytes written
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileWrite',
+  'data': { 'count': 'int', 'eof': 'bool' } }
+{ 'command': 'guest-file-write',
+  'data':    { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' },
+  'returns': 'GuestFileWrite' }
+
+##
+# @guest-file-seek:
+#
+# Seek to a position in the file, as with fseek(), and return the
+# current file position afterward. Also encapsulates ftell()'s
+# functionality, just Set offset=0, whence=SEEK_CUR.
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @offset: bytes to skip over in the file stream
+#
+# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek()
+#
+# Returns: GuestFileSeek on success.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileSeek',
+  'data': { 'position': 'int', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-seek',
+  'data':    { 'handle': 'int', 'offset': 'int', 'whence': 'int' },
+  'returns': 'GuestFileSeek' }
+
+##
+# @guest-file-flush:
+#
+# Write file changes bufferred in userspace to disk/kernel buffers
+#
+# @handle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-flush',
+  'data': { 'handle': 'int' } }
+
+##
+# @guest-fsfreeze-status:
+#
+# Get guest fsfreeze state. error state indicates failure to thaw 1 or more
+# previously frozen filesystems, or failure to open a previously cached
+# filesytem (filesystem unmounted/directory changes, etc).
+#
+# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
+#
+# Since: 0.15.0
+##
+{ 'enum': 'GuestFsfreezeStatus',
+  'data': [ 'thawed', 'frozen', 'error' ] }
+{ 'command': 'guest-fsfreeze-status',
+  'returns': 'GuestFsfreezeStatus' }
+
+##
+# @guest-fsfreeze-freeze:
+#
+# Sync and freeze all non-network guest filesystems
+#
+# Returns: Number of file systems frozen on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-fsfreeze-freeze',
+  'returns': 'int' }
+
+##
+# @guest-fsfreeze-thaw:
+#
+# Unfreeze frozen guest fileystems
+#
+# Returns: Number of file systems thawed
+#          If error, -1 (unknown error) or -errno
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-fsfreeze-thaw',
+  'returns': 'int' }
diff --git a/qemu-ga.c b/qemu-ga.c
index eb09100..4530d3d 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -653,6 +653,9 @@ int main(int argc, char **argv)
     g_log_set_default_handler(ga_log, s);
     g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
     s->logging_enabled = true;
+    s->command_state = ga_command_state_new();
+    ga_command_state_init(s, s->command_state);
+    ga_command_state_init_all(s->command_state);
     ga_state = s;
 
     module_call_init(MODULE_INIT_QAPI);
@@ -661,6 +664,7 @@ int main(int argc, char **argv)
 
     g_main_loop_run(ga_state->main_loop);
 
+    ga_command_state_cleanup_all(ga_state->command_state);
     unlink(pidfile);
 
     return 0;
diff --git a/qerror.c b/qerror.c
index c92adfc..229d0d6 100644
--- a/qerror.c
+++ b/qerror.c
@@ -218,6 +218,14 @@ static const QErrorStringTable qerror_table[] = {
         .error_fmt = QERR_VNC_SERVER_FAILED,
         .desc      = "Could not start VNC server on %(target)",
     },
+    {
+        .error_fmt = QERR_QGA_LOGGING_FAILED,
+        .desc      = "Guest agent failed to log non-optional log statement",
+    },
+    {
+        .error_fmt = QERR_QGA_COMMAND_FAILED,
+        .desc      = "Guest agent command failed, error was '%(message)'",
+    },
     {}
 };
 
diff --git a/qerror.h b/qerror.h
index 9a9fa5b..7ec0fc1 100644
--- a/qerror.h
+++ b/qerror.h
@@ -184,4 +184,10 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_FEATURE_DISABLED \
     "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }"
 
+#define QERR_QGA_LOGGING_FAILED \
+    "{ 'class': 'QgaLoggingFailed', 'data': {} }"
+
+#define QERR_QGA_COMMAND_FAILED \
+    "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }"
+
 #endif /* QERROR_H */
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
new file mode 100644
index 0000000..8c0d67e
--- /dev/null
+++ b/qga/guest-agent-commands.c
@@ -0,0 +1,518 @@
+/*
+ * QEMU Guest Agent commands
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Michael Roth      <mdroth@linux.vnet.ibm.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 <mntent.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include "qga/guest-agent-core.h"
+#include "qga-qmp-commands.h"
+#include "qerror.h"
+#include "qemu-queue.h"
+
+static GAState *ga_state;
+
+static void disable_logging(void)
+{
+    ga_disable_logging(ga_state);
+}
+
+static void enable_logging(void)
+{
+    ga_enable_logging(ga_state);
+}
+
+/* Note: in some situations, like with the fsfreeze, logging may be
+ * temporarilly disabled. if it is necessary that a command be able
+ * to log for accounting purposes, check ga_logging_enabled() beforehand,
+ * and use the QERR_QGA_LOGGING_DISABLED to generate an error
+ */
+static void slog(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
+    va_end(ap);
+}
+
+int64_t qmp_guest_sync(int64_t id, Error **errp)
+{
+    return id;
+}
+
+void qmp_guest_ping(Error **err)
+{
+    slog("guest-ping called");
+}
+
+struct GuestAgentInfo *qmp_guest_info(Error **err)
+{
+    GuestAgentInfo *info = qemu_mallocz(sizeof(GuestAgentInfo));
+
+    info->version = g_strdup(QGA_VERSION);
+
+    return info;
+}
+
+void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
+{
+    int ret;
+    const char *shutdown_flag;
+
+    slog("guest-shutdown called, mode: %s", mode);
+    if (!has_mode || strcmp(mode, "powerdown") == 0) {
+        shutdown_flag = "-P";
+    } else if (strcmp(mode, "halt") == 0) {
+        shutdown_flag = "-H";
+    } else if (strcmp(mode, "reboot") == 0) {
+        shutdown_flag = "-r";
+    } else {
+        error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
+                  "halt|powerdown|reboot");
+        return;
+    }
+
+    ret = fork();
+    if (ret == 0) {
+        /* child, start the shutdown */
+        setsid();
+        fclose(stdin);
+        fclose(stdout);
+        fclose(stderr);
+
+        ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
+                    "hypervisor initiated shutdown", (char*)NULL);
+        if (ret) {
+            slog("guest-shutdown failed: %s", strerror(errno));
+        }
+        exit(!!ret);
+    } else if (ret < 0) {
+        error_set(err, QERR_UNDEFINED_ERROR);
+    }
+}
+
+typedef struct GuestFileHandle {
+    uint64_t id;
+    FILE *fh;
+    QTAILQ_ENTRY(GuestFileHandle) next;
+} GuestFileHandle;
+
+static struct {
+    QTAILQ_HEAD(, GuestFileHandle) filehandles;
+} guest_file_state;
+
+static void guest_file_handle_add(FILE *fh)
+{
+    GuestFileHandle *gfh;
+
+    gfh = qemu_mallocz(sizeof(GuestFileHandle));
+    gfh->id = fileno(fh);
+    gfh->fh = fh;
+    QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
+}
+
+static GuestFileHandle *guest_file_handle_find(int64_t id)
+{
+    GuestFileHandle *gfh;
+
+    QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
+    {
+        if (gfh->id == id) {
+            return gfh;
+        }
+    }
+
+    return NULL;
+}
+
+int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
+{
+    FILE *fh;
+    int fd;
+    int64_t ret = -1;
+
+    if (!has_mode) {
+        mode = "r";
+    }
+    slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
+    fh = fopen(path, mode);
+    if (!fh) {
+        error_set(err, QERR_OPEN_FILE_FAILED, path);
+        return -1;
+    }
+
+    /* set fd non-blocking to avoid common use cases (like reading from a
+     * named pipe) from hanging the agent
+     */
+    fd = fileno(fh);
+    ret = fcntl(fd, F_GETFL);
+    ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
+    if (ret == -1) {
+        error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
+        fclose(fh);
+        return -1;
+    }
+
+    guest_file_handle_add(fh);
+    slog("guest-file-open, handle: %d", fd);
+    return fd;
+}
+
+void qmp_guest_file_close(int64_t handle, Error **err)
+{
+    GuestFileHandle *gfh = guest_file_handle_find(handle);
+    int ret;
+
+    slog("guest-file-close called, handle: %ld", handle);
+    if (!gfh) {
+        error_set(err, QERR_FD_NOT_FOUND, "handle");
+        return;
+    }
+
+    ret = fclose(gfh->fh);
+    if (ret == -1) {
+        error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
+        return;
+    }
+
+    QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
+    qemu_free(gfh);
+}
+
+struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
+                                          int64_t count, Error **err)
+{
+    GuestFileHandle *gfh = guest_file_handle_find(handle);
+    GuestFileRead *read_data = NULL;
+    guchar *buf;
+    FILE *fh;
+    size_t read_count;
+
+    if (!gfh) {
+        error_set(err, QERR_FD_NOT_FOUND, "handle");
+        return NULL;
+    }
+
+    if (!has_count) {
+        count = QGA_READ_COUNT_DEFAULT;
+    } else if (count < 0) {
+        error_set(err, QERR_INVALID_PARAMETER, "count");
+        return NULL;
+    }
+
+    fh = gfh->fh;
+    buf = qemu_mallocz(count+1);
+    read_count = fread(buf, 1, count, fh);
+    if (ferror(fh)) {
+        slog("guest-file-read failed, handle: %ld", handle);
+        error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
+    } else {
+        buf[read_count] = 0;
+        read_data = qemu_mallocz(sizeof(GuestFileRead));
+        read_data->count = read_count;
+        read_data->eof = feof(fh);
+        if (read_count) {
+            read_data->buf_b64 = g_base64_encode(buf, read_count);
+        }
+    }
+    qemu_free(buf);
+    clearerr(fh);
+
+    return read_data;
+}
+
+GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
+                                     bool has_count, int64_t count, Error **err)
+{
+    GuestFileWrite *write_data = NULL;
+    guchar *buf;
+    gsize buf_len;
+    int write_count;
+    GuestFileHandle *gfh = guest_file_handle_find(handle);
+    FILE *fh;
+
+    if (!gfh) {
+        error_set(err, QERR_FD_NOT_FOUND, "handle");
+        return NULL;
+    }
+
+    fh = gfh->fh;
+    buf = g_base64_decode(buf_b64, &buf_len);
+
+    if (!has_count) {
+        count = buf_len;
+    } else if (count < 0 || count > buf_len) {
+        qemu_free(buf);
+        error_set(err, QERR_INVALID_PARAMETER, "count");
+        return NULL;
+    }
+
+    write_count = fwrite(buf, 1, count, fh);
+    if (ferror(fh)) {
+        slog("guest-file-write failed, handle: %ld", handle);
+        error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
+    } else {
+        write_data = qemu_mallocz(sizeof(GuestFileWrite));
+        write_data->count = write_count;
+        write_data->eof = feof(fh);
+    }
+    qemu_free(buf);
+    clearerr(fh);
+
+    return write_data;
+}
+
+struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
+                                          int64_t whence, Error **err)
+{
+    GuestFileHandle *gfh = guest_file_handle_find(handle);
+    GuestFileSeek *seek_data = NULL;
+    FILE *fh;
+    int ret;
+
+    if (!gfh) {
+        error_set(err, QERR_FD_NOT_FOUND, "handle");
+        return NULL;
+    }
+
+    fh = gfh->fh;
+    ret = fseek(fh, offset, whence);
+    if (ret == -1) {
+        error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+    } else {
+        seek_data = qemu_mallocz(sizeof(GuestFileRead));
+        seek_data->position = ftell(fh);
+        seek_data->eof = feof(fh);
+    }
+    clearerr(fh);
+
+    return seek_data;
+}
+
+void qmp_guest_file_flush(int64_t handle, Error **err)
+{
+    GuestFileHandle *gfh = guest_file_handle_find(handle);
+    FILE *fh;
+    int ret;
+
+    if (!gfh) {
+        error_set(err, QERR_FD_NOT_FOUND, "handle");
+        return;
+    }
+
+    fh = gfh->fh;
+    ret = fflush(fh);
+    if (ret == EOF) {
+        error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+    }
+}
+
+static void guest_file_init(void)
+{
+    QTAILQ_INIT(&guest_file_state.filehandles);
+}
+
+typedef struct GuestFsfreezeMount {
+    char *dirname;
+    char *devtype;
+    QTAILQ_ENTRY(GuestFsfreezeMount) next;
+} GuestFsfreezeMount;
+
+struct {
+    GuestFsfreezeStatus status;
+    QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
+} guest_fsfreeze_state;
+
+/*
+ * Walk the mount table and build a list of local file systems
+ */
+static int guest_fsfreeze_build_mount_list(void)
+{
+    struct mntent *ment;
+    GuestFsfreezeMount *mount, *temp;
+    char const *mtab = MOUNTED;
+    FILE *fp;
+
+    QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+        QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
+        qemu_free(mount->dirname);
+        qemu_free(mount->devtype);
+        qemu_free(mount);
+    }
+
+    fp = setmntent(mtab, "r");
+    if (!fp) {
+        g_warning("fsfreeze: unable to read mtab");
+        return -1;
+    }
+
+    while ((ment = getmntent(fp))) {
+        /*
+         * An entry which device name doesn't start with a '/' is
+         * either a dummy file system or a network file system.
+         * Add special handling for smbfs and cifs as is done by
+         * coreutils as well.
+         */
+        if ((ment->mnt_fsname[0] != '/') ||
+            (strcmp(ment->mnt_type, "smbfs") == 0) ||
+            (strcmp(ment->mnt_type, "cifs") == 0)) {
+            continue;
+        }
+
+        mount = qemu_mallocz(sizeof(GuestFsfreezeMount));
+        mount->dirname = qemu_strdup(ment->mnt_dir);
+        mount->devtype = qemu_strdup(ment->mnt_type);
+
+        QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
+    }
+
+    endmntent(fp);
+
+    return 0;
+}
+
+/*
+ * Return status of freeze/thaw
+ */
+GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
+{
+    return guest_fsfreeze_state.status;
+}
+
+/*
+ * Walk list of mounted file systems in the guest, and freeze the ones which
+ * are real local file systems.
+ */
+int64_t qmp_guest_fsfreeze_freeze(Error **err)
+{
+    int ret = 0, i = 0;
+    struct GuestFsfreezeMount *mount, *temp;
+    int fd;
+    char err_msg[512];
+
+    slog("guest-fsfreeze called");
+
+    if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+        return 0;
+    }
+
+    ret = guest_fsfreeze_build_mount_list();
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* cannot risk guest agent blocking itself on a write in this state */
+    disable_logging();
+
+    QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+        fd = qemu_open(mount->dirname, O_RDONLY);
+        if (fd == -1) {
+            sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
+            error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+            goto error;
+        }
+
+        /* we try to cull filesytems we know won't work in advance, but other
+         * filesytems may not implement fsfreeze for less obvious reasons.
+         * these will report EOPNOTSUPP, so we simply ignore them. when
+         * thawing, these filesystems will return an EINVAL instead, due to
+         * not being in a frozen state. Other filesystem-specific
+         * errors may result in EINVAL, however, so the user should check the
+         * number * of filesystems returned here against those returned by the
+         * thaw operation to determine whether everything completed
+         * successfully
+         */
+        ret = ioctl(fd, FIFREEZE);
+        if (ret < 0 && errno != EOPNOTSUPP) {
+            sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
+            error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+            close(fd);
+            goto error;
+        }
+        close(fd);
+
+        i++;
+    }
+
+    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
+    return i;
+
+error:
+    if (i > 0) {
+        qmp_guest_fsfreeze_thaw(NULL);
+    }
+    return 0;
+}
+
+/*
+ * Walk list of frozen file systems in the guest, and thaw them.
+ */
+int64_t qmp_guest_fsfreeze_thaw(Error **err)
+{
+    int ret;
+    GuestFsfreezeMount *mount, *temp;
+    int fd, i = 0;
+    bool has_error = false;
+
+    QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+        fd = qemu_open(mount->dirname, O_RDONLY);
+        if (fd == -1) {
+            has_error = true;
+            continue;
+        }
+        ret = ioctl(fd, FITHAW);
+        if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
+            has_error = true;
+            close(fd);
+            continue;
+        }
+        close(fd);
+        i++;
+    }
+
+    if (has_error) {
+        guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
+    } else {
+        guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
+    }
+    enable_logging();
+    return i;
+}
+
+static void guest_fsfreeze_init(void)
+{
+    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
+    QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
+}
+
+static void guest_fsfreeze_cleanup(void)
+{
+    int64_t ret;
+    Error *err = NULL;
+
+    if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+        ret = qmp_guest_fsfreeze_thaw(&err);
+        if (ret < 0 || err) {
+            slog("failed to clean up frozen filesystems");
+        }
+    }
+}
+
+/* register init/cleanup routines for stateful command groups */
+void ga_command_state_init(GAState *s, GACommandState *cs)
+{
+    ga_state = s;
+    ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
+    ga_command_state_add(cs, guest_file_init, NULL);
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index 66d1729..e42b91d 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -14,10 +14,12 @@
 #include "qemu-common.h"
 
 #define QGA_VERSION "1.0"
+#define QGA_READ_COUNT_DEFAULT 4 << 10
 
 typedef struct GAState GAState;
 typedef struct GACommandState GACommandState;
 
+void ga_command_state_init(GAState *s, GACommandState *cs);
 void ga_command_state_add(GACommandState *cs,
                           void (*init)(void),
                           void (*cleanup)(void));
-- 
1.7.0.4

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

* Re: [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon Michael Roth
@ 2011-07-20 17:56   ` Luiz Capitulino
  2011-07-20 19:25     ` Michael Roth
  2011-07-20 20:00   ` [Qemu-devel] [PATCH] guest agent: qemu-ga, remove unused variables Michael Roth
  2011-07-20 20:19   ` [Qemu-devel] [PATCH] guest agent: qemu-ga daemon Michael Roth
  2 siblings, 1 reply; 30+ messages in thread
From: Luiz Capitulino @ 2011-07-20 17:56 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, agl, qemu-devel, Jes.Sorensen

On Tue, 19 Jul 2011 15:41:54 -0500
Michael Roth <mdroth@linux.vnet.ibm.com> wrote:

> This is the actual guest daemon, it listens for requests over a
> virtio-serial/isa-serial/unix socket channel and routes them through
> to dispatch routines, and writes the results back to the channel in
> a manner similar to QMP.
> 
> A shorthand invocation:
> 
>   qemu-ga -d
> 
> Is equivalent to:
> 
>   qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>           -f /var/run/qemu-ga.pid -d
> 
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> ---
>  Makefile               |    8 +-
>  configure              |    1 +
>  qemu-ga.c              |  667 ++++++++++++++++++++++++++++++++++++++++++++++++
>  qga/guest-agent-core.h |    4 +
>  4 files changed, 677 insertions(+), 3 deletions(-)
>  create mode 100644 qemu-ga.c
> 
> diff --git a/Makefile b/Makefile
> index b8cdf0e..0d2e33d 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -140,7 +140,7 @@ endif
>  ######################################################################
>  
>  qemu-img.o: qemu-img-cmds.h
> -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
> +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
>  
>  qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
>  
> @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o
>  
>  QGALIB=qga/guest-agent-command-state.o
>  
> +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
> +
>  QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
>  
>  clean:
>  # avoid old build problems by removing potentially incorrect old files
>  	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
>  	rm -f qemu-options.def
> -	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
> +	rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
>  	rm -Rf .libs
>  	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
>  	rm -f qemu-img-cmds.h
> @@ -386,4 +388,4 @@ tarbin:
>  	$(mandir)/man8/qemu-nbd.8
>  
>  # Include automatically generated dependency files
> --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d)
> +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
> diff --git a/configure b/configure
> index 6a03002..445f674 100755
> --- a/configure
> +++ b/configure
> @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
>    tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
>    if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
>        tools="qemu-nbd\$(EXESUF) $tools"
> +      tools="qemu-ga\$(EXESUF) $tools"
>      if [ "$check_utests" = "yes" ]; then
>        tools="check-qint check-qstring check-qdict check-qlist $tools"
>        tools="check-qfloat check-qjson $tools"
> diff --git a/qemu-ga.c b/qemu-ga.c
> new file mode 100644
> index 0000000..eb09100
> --- /dev/null
> +++ b/qemu-ga.c
> @@ -0,0 +1,667 @@
> +/*
> + * QEMU Guest Agent
> + *
> + * Copyright IBM Corp. 2011
> + *
> + * Authors:
> + *  Adam Litke        <aglitke@linux.vnet.ibm.com>
> + *  Michael Roth      <mdroth@linux.vnet.ibm.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 <stdlib.h>
> +#include <stdio.h>
> +#include <stdbool.h>
> +#include <glib.h>
> +#include <gio/gio.h>
> +#include <getopt.h>
> +#include <termios.h>
> +#include <syslog.h>
> +#include "qemu_socket.h"
> +#include "json-streamer.h"
> +#include "json-parser.h"
> +#include "qint.h"
> +#include "qjson.h"
> +#include "qga/guest-agent-core.h"
> +#include "module.h"
> +#include "signal.h"
> +#include "qerror.h"
> +#include "error_int.h"
> +
> +#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
> +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
> +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
> +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
> +
> +struct GAState {
> +    JSONMessageParser parser;
> +    GMainLoop *main_loop;
> +    guint conn_id;
> +    GSocket *conn_sock;
> +    GIOChannel *conn_channel;
> +    guint listen_id;
> +    GSocket *listen_sock;
> +    GIOChannel *listen_channel;
> +    const char *path;
> +    const char *method;
> +    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
> +    GACommandState *command_state;
> +    GLogLevelFlags log_level;
> +    FILE *log_file;
> +    bool logging_enabled;
> +};
> +
> +static struct GAState *ga_state;
> +
> +static void quit_handler(int sig)
> +{
> +    g_debug("recieved signal num %d, quitting", sig);
> +
> +    if (g_main_loop_is_running(ga_state->main_loop)) {
> +        g_main_loop_quit(ga_state->main_loop);
> +    }
> +}
> +
> +static void register_signal_handlers(void)
> +{
> +    struct sigaction sigact;
> +    int ret;
> +
> +    memset(&sigact, 0, sizeof(struct sigaction));
> +    sigact.sa_handler = quit_handler;
> +
> +    ret = sigaction(SIGINT, &sigact, NULL);
> +    if (ret == -1) {
> +        g_error("error configuring signal handler: %s", strerror(errno));
> +        exit(EXIT_FAILURE);
> +    }
> +    ret = sigaction(SIGTERM, &sigact, NULL);
> +    if (ret == -1) {
> +        g_error("error configuring signal handler: %s", strerror(errno));
> +    }
> +}
> +
> +static void usage(const char *cmd)
> +{
> +    printf(
> +"Usage: %s -c <channel_opts>\n"
> +"QEMU Guest Agent %s\n"
> +"\n"
> +"  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
> +"                    isa-serial (virtio-serial is the default)\n"
> +"  -p, --path        device/socket path (%s is the default for virtio-serial)\n"
> +"  -l, --logfile     set logfile path, logs to stderr by default\n"
> +"  -f, --pidfile     specify pidfile (default is %s)\n"
> +"  -v, --verbose     log extra debugging information\n"
> +"  -V, --version     print version information and exit\n"
> +"  -d, --daemonize   become a daemon\n"
> +"  -h, --help        display this help and exit\n"
> +"\n"
> +"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
> +    , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
> +}
> +
> +static void conn_channel_close(GAState *s);
> +
> +static const char *ga_log_level_str(GLogLevelFlags level)
> +{
> +    switch (level & G_LOG_LEVEL_MASK) {
> +        case G_LOG_LEVEL_ERROR:
> +            return "error";
> +        case G_LOG_LEVEL_CRITICAL:
> +            return "critical";
> +        case G_LOG_LEVEL_WARNING:
> +            return "warning";
> +        case G_LOG_LEVEL_MESSAGE:
> +            return "message";
> +        case G_LOG_LEVEL_INFO:
> +            return "info";
> +        case G_LOG_LEVEL_DEBUG:
> +            return "debug";
> +        default:
> +            return "user";
> +    }
> +}
> +
> +bool ga_logging_enabled(GAState *s)
> +{
> +    return s->logging_enabled;
> +}
> +
> +void ga_disable_logging(GAState *s)
> +{
> +    s->logging_enabled = false;
> +}
> +
> +void ga_enable_logging(GAState *s)
> +{
> +    s->logging_enabled = true;
> +}
> +
> +static void ga_log(const gchar *domain, GLogLevelFlags level,
> +                   const gchar *msg, gpointer opaque)
> +{
> +    GAState *s = opaque;
> +    GTimeVal time;
> +    const char *level_str = ga_log_level_str(level);
> +
> +    if (!ga_logging_enabled(s)) {
> +        return;
> +    }
> +
> +    level &= G_LOG_LEVEL_MASK;
> +    if (g_strcmp0(domain, "syslog") == 0) {
> +        syslog(LOG_INFO, "%s: %s", level_str, msg);
> +    } else if (level & s->log_level) {
> +        g_get_current_time(&time);
> +        fprintf(s->log_file,
> +                "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
> +        fflush(s->log_file);
> +    }
> +}
> +
> +static void become_daemon(const char *pidfile)
> +{
> +    pid_t pid, sid;
> +    int pidfd;
> +    char *pidstr = NULL;
> +
> +    pid = fork();
> +    if (pid < 0) {
> +        exit(EXIT_FAILURE);
> +    }
> +    if (pid > 0) {
> +        exit(EXIT_SUCCESS);
> +    }
> +
> +    pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
> +    if (pidfd == -1) {
> +        g_critical("Cannot create pid file, %s", strerror(errno));
> +        exit(EXIT_FAILURE);
> +    }
> +
> +    if (asprintf(&pidstr, "%d", getpid()) == -1) {
> +        g_critical("Cannot allocate memory");
> +        goto fail;
> +    }
> +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
> +        free(pidstr);
> +        g_critical("Failed to write pid file");
> +        goto fail;
> +    }
> +
> +    umask(0);
> +    sid = setsid();
> +    if (sid < 0) {
> +        goto fail;
> +    }
> +    if ((chdir("/")) < 0) {
> +        goto fail;
> +    }
> +
> +    close(STDIN_FILENO);
> +    close(STDOUT_FILENO);
> +    close(STDERR_FILENO);
> +    free(pidstr);
> +    return;
> +
> +fail:
> +    unlink(pidfile);
> +    g_critical("failed to daemonize");
> +    exit(EXIT_FAILURE);
> +}
> +
> +static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
> +                                 gsize count)
> +{
> +    GError *err = NULL;
> +    gsize written = 0;
> +    GIOStatus status;
> +
> +    while (count) {
> +        status = g_io_channel_write_chars(channel, buf, count, &written, &err);
> +        g_debug("sending data, count: %d", (int)count);
> +        if (err != NULL) {
> +            g_warning("error sending newline: %s", err->message);
> +            return err->code;
> +        }
> +        if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
> +            return -EPIPE;
> +        }
> +
> +        if (status == G_IO_STATUS_NORMAL) {
> +            count -= written;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
> +{
> +    int ret = 0;
> +    const char *buf;
> +    QString *payload_qstr;
> +    GError *err = NULL;
> +
> +    g_assert(payload && channel);
> +
> +    payload_qstr = qobject_to_json(payload);
> +    if (!payload_qstr) {
> +        return -EINVAL;
> +    }
> +
> +    qstring_append_chr(payload_qstr, '\n');
> +    buf = qstring_get_str(payload_qstr);
> +    ret = conn_channel_send_buf(channel, buf, strlen(buf));
> +    if (ret) {
> +        goto out_free;
> +    }
> +
> +    g_io_channel_flush(channel, &err);
> +    if (err != NULL) {
> +        g_warning("error flushing payload: %s", err->message);
> +        ret = err->code;
> +        goto out_free;
> +    }
> +
> +out_free:
> +    QDECREF(payload_qstr);
> +    if (err) {
> +        g_error_free(err);
> +    }
> +    return ret;
> +}
> +
> +static void process_command(GAState *s, QDict *req)
> +{
> +    QObject *rsp = NULL;
> +    int ret;
> +
> +    g_assert(req);
> +    g_debug("processing command");
> +    rsp = qmp_dispatch(QOBJECT(req));
> +    if (rsp) {
> +        ret = conn_channel_send_payload(s->conn_channel, rsp);
> +        if (ret) {
> +            g_warning("error sending payload: %s", strerror(ret));
> +        }
> +        qobject_decref(rsp);
> +    } else {
> +        g_warning("error getting response");
> +    }
> +}
> +
> +/* handle requests/control events coming in over the channel */
> +static void process_event(JSONMessageParser *parser, QList *tokens)
> +{
> +    GAState *s = container_of(parser, GAState, parser);
> +    QObject *obj;
> +    QDict *qdict;
> +    Error *err = NULL;
> +    int ret;
> +
> +    g_assert(s && parser);
> +
> +    g_debug("process_event: called");
> +    obj = json_parser_parse_err(tokens, NULL, &err);
> +    if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
> +        qobject_decref(obj);
> +        qdict = qdict_new();
> +        if (!err) {
> +            g_warning("failed to parse event: unknown error");
> +            error_set(&err, QERR_JSON_PARSING);
> +        } else {
> +            g_warning("failed to parse event: %s", error_get_pretty(err));
> +        }
> +        qdict_put_obj(qdict, "error", error_get_qobject(err));
> +        error_free(err);
> +    } else {
> +        qdict = qobject_to_qdict(obj);
> +    }
> +
> +    g_assert(qdict);
> +
> +    /* handle host->guest commands */
> +    if (qdict_haskey(qdict, "execute")) {
> +        process_command(s, qdict);
> +    } else {
> +        if (!qdict_haskey(qdict, "error")) {
> +            QDECREF(qdict);
> +            qdict = qdict_new();
> +            g_warning("unrecognized payload format");
> +            error_set(&err, QERR_UNSUPPORTED);
> +            qdict_put_obj(qdict, "error", error_get_qobject(err));
> +            error_free(err);
> +        }
> +        ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
> +        if (ret) {
> +            g_warning("error sending payload: %s", strerror(ret));
> +        }
> +    }
> +
> +    QDECREF(qdict);
> +}
> +
> +static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
> +                                  gpointer data)
> +{
> +    GAState *s = data;
> +    gchar buf[1024];
> +    gsize count;
> +    GError *err = NULL;
> +    memset(buf, 0, 1024);
> +    GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
> +                                               &count, &err);
> +    if (err != NULL) {
> +        g_warning("error reading channel: %s", err->message);
> +        conn_channel_close(s);
> +        g_error_free(err);
> +        return false;
> +    }
> +    switch (status) {
> +    case G_IO_STATUS_ERROR:
> +        g_warning("problem");
> +        return false;
> +    case G_IO_STATUS_NORMAL:
> +        g_debug("read data, count: %d, data: %s", (int)count, buf);
> +        json_message_parser_feed(&s->parser, (char *)buf, (int)count);
> +    case G_IO_STATUS_AGAIN:
> +        /* virtio causes us to spin here when no process is attached to
> +         * host-side chardev. sleep a bit to mitigate this
> +         */
> +        if (s->virtio) {
> +            usleep(100*1000);
> +        }
> +        return true;
> +    case G_IO_STATUS_EOF:
> +        g_debug("received EOF");
> +        conn_channel_close(s);
> +        if (s->virtio) {
> +            return true;
> +        }
> +        return false;
> +    default:
> +        g_warning("unknown channel read status, closing");
> +        conn_channel_close(s);
> +        return false;
> +    }
> +    return true;
> +}
> +
> +static int conn_channel_add(GAState *s, int fd)
> +{
> +    GIOChannel *conn_channel;
> +    guint conn_id;
> +    GError *err = NULL;
> +
> +    g_assert(s && !s->conn_channel);
> +    conn_channel = g_io_channel_unix_new(fd);
> +    g_assert(conn_channel);
> +    g_io_channel_set_encoding(conn_channel, NULL, &err);
> +    if (err != NULL) {
> +        g_warning("error setting channel encoding to binary");
> +        g_error_free(err);
> +        return -1;
> +    }
> +    conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
> +                             conn_channel_read, s);
> +    if (err != NULL) {
> +        g_warning("error adding io watch: %s", err->message);
> +        g_error_free(err);
> +        return -1;
> +    }
> +    s->conn_channel = conn_channel;
> +    s->conn_id = conn_id;
> +    return 0;
> +}
> +
> +static gboolean listen_channel_accept(GIOChannel *channel,
> +                                      GIOCondition condition, gpointer data)
> +{
> +    GAState *s = data;
> +    GError *err = NULL;
> +    g_assert(channel != NULL);
> +    int ret;
> +    bool accepted = false;
> +
> +    s->conn_sock = g_socket_accept(s->listen_sock, NULL, &err);
> +    if (err != NULL) {
> +        g_warning("error converting fd to gsocket: %s", err->message);
> +        g_error_free(err);
> +        goto out;
> +    }
> +    ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
> +    if (ret) {
> +        g_warning("error setting up connection");
> +        goto out;
> +    }
> +    accepted = true;
> +
> +out:
> +    /* only accept 1 connection at a time */
> +    return !accepted;
> +}
> +
> +/* start polling for readable events on listen fd, new==true
> + * indicates we should use the existing s->listen_channel
> + */
> +static int listen_channel_add(GAState *s, int listen_fd, bool new)
> +{
> +    GError *err = NULL;
> +    guint listen_id;
> +
> +    if (new) {
> +        s->listen_channel = g_io_channel_unix_new(listen_fd);
> +        if (s->listen_sock) {
> +            g_object_unref(s->listen_sock);
> +        }
> +        s->listen_sock = g_socket_new_from_fd(listen_fd, &err);
> +        if (err != NULL) {
> +            g_warning("error converting fd to gsocket: %s", err->message);
> +            g_error_free(err);
> +            return -1;
> +        }
> +    }
> +    listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
> +                               listen_channel_accept, s);
> +    if (err != NULL) {
> +        g_warning("error adding io watch: %s", err->message);
> +        g_error_free(err);
> +        return -1;
> +    }
> +    return 0;

I'm getting the following error when building this on a F15 guest:

qemu-ga.c: In function ‘listen_channel_add’:
qemu-ga.c:453:11: error: variable ‘listen_id’ set but not used [-Werror=unused-but-set-variable]
cc1: all warnings being treated as errors

g_io_add_watch() doesn't seem to return an error. So if the fix is just to
drop 'listen_id' and the last if statement altogether I can do it myself,
otherwise you can send me only this patch.

The series looks good otherwise.

> +}
> +
> +/* cleanup state for closed connection/session, start accepting new
> + * connections if we're in listening mode
> + */
> +static void conn_channel_close(GAState *s)
> +{
> +    if (strcmp(s->method, "unix-listen") == 0) {
> +        g_io_channel_shutdown(s->conn_channel, true, NULL);
> +        g_object_unref(s->conn_sock);
> +        s->conn_sock = NULL;
> +        listen_channel_add(s, 0, false);
> +    } else if (strcmp(s->method, "virtio-serial") == 0) {
> +        /* we spin on EOF for virtio-serial, so back off a bit. also,
> +         * dont close the connection in this case, it'll resume normal
> +         * operation when another process connects to host chardev
> +         */
> +        usleep(100*1000);
> +        goto out_noclose;
> +    }
> +    g_io_channel_unref(s->conn_channel);
> +    s->conn_channel = NULL;
> +    s->conn_id = 0;
> +out_noclose:
> +    return;
> +}
> +
> +static void init_guest_agent(GAState *s)
> +{
> +    struct termios tio;
> +    int ret, fd;
> +
> +    if (s->method == NULL) {
> +        /* try virtio-serial as our default */
> +        s->method = "virtio-serial";
> +    }
> +
> +    if (s->path == NULL) {
> +        if (strcmp(s->method, "virtio-serial") != 0) {
> +            g_critical("must specify a path for this channel");
> +            exit(EXIT_FAILURE);
> +        }
> +        /* try the default path for the virtio-serial port */
> +        s->path = QGA_VIRTIO_PATH_DEFAULT;
> +    }
> +
> +    if (strcmp(s->method, "virtio-serial") == 0) {
> +        s->virtio = true;
> +        fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
> +        if (fd == -1) {
> +            g_critical("error opening channel: %s", strerror(errno));
> +            exit(EXIT_FAILURE);
> +        }
> +        ret = conn_channel_add(s, fd);
> +        if (ret) {
> +            g_critical("error adding channel to main loop");
> +            exit(EXIT_FAILURE);
> +        }
> +    } else if (strcmp(s->method, "isa-serial") == 0) {
> +        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
> +        if (fd == -1) {
> +            g_critical("error opening channel: %s", strerror(errno));
> +            exit(EXIT_FAILURE);
> +        }
> +        tcgetattr(fd, &tio);
> +        /* set up serial port for non-canonical, dumb byte streaming */
> +        tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
> +                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
> +                         IMAXBEL);
> +        tio.c_oflag = 0;
> +        tio.c_lflag = 0;
> +        tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
> +        /* 1 available byte min or reads will block (we'll set non-blocking
> +         * elsewhere, else we have to deal with read()=0 instead)
> +         */
> +        tio.c_cc[VMIN] = 1;
> +        tio.c_cc[VTIME] = 0;
> +        /* flush everything waiting for read/xmit, it's garbage at this point */
> +        tcflush(fd, TCIFLUSH);
> +        tcsetattr(fd, TCSANOW, &tio);
> +        ret = conn_channel_add(s, fd);
> +        if (ret) {
> +            g_error("error adding channel to main loop");
> +        }
> +    } else if (strcmp(s->method, "unix-listen") == 0) {
> +        fd = unix_listen(s->path, NULL, strlen(s->path));
> +        if (fd == -1) {
> +            g_critical("error opening path: %s", strerror(errno));
> +            exit(EXIT_FAILURE);
> +        }
> +        ret = listen_channel_add(s, fd, true);
> +        if (ret) {
> +            g_critical("error binding/listening to specified socket");
> +            exit(EXIT_FAILURE);
> +        }
> +    } else {
> +        g_critical("unsupported channel method/type: %s", s->method);
> +        exit(EXIT_FAILURE);
> +    }
> +
> +    json_message_parser_init(&s->parser, process_event);
> +    s->main_loop = g_main_loop_new(NULL, false);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    const char *sopt = "hVvdm:p:l:f:";
> +    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
> +    const struct option lopt[] = {
> +        { "help", 0, NULL, 'h' },
> +        { "version", 0, NULL, 'V' },
> +        { "logfile", 0, NULL, 'l' },
> +        { "pidfile", 0, NULL, 'f' },
> +        { "verbose", 0, NULL, 'v' },
> +        { "method", 0, NULL, 'm' },
> +        { "path", 0, NULL, 'p' },
> +        { "daemonize", 0, NULL, 'd' },
> +        { NULL, 0, NULL, 0 }
> +    };
> +    int opt_ind = 0, ch, daemonize = 0;
> +    GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
> +    FILE *log_file = stderr;
> +    GAState *s;
> +
> +    while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
> +        switch (ch) {
> +        case 'm':
> +            method = optarg;
> +            break;
> +        case 'p':
> +            path = optarg;
> +            break;
> +        case 'l':
> +            log_file = fopen(optarg, "a");
> +            if (!log_file) {
> +                g_critical("unable to open specified log file: %s",
> +                           strerror(errno));
> +                return EXIT_FAILURE;
> +            }
> +            break;
> +        case 'f':
> +            pidfile = optarg;
> +            break;
> +        case 'v':
> +            /* enable all log levels */
> +            log_level = G_LOG_LEVEL_MASK;
> +            break;
> +        case 'V':
> +            printf("QEMU Guest Agent %s\n", QGA_VERSION);
> +            return 0;
> +        case 'd':
> +            daemonize = 1;
> +            break;
> +        case 'h':
> +            usage(argv[0]);
> +            return 0;
> +        case '?':
> +            g_print("Unknown option, try '%s --help' for more information.\n",
> +                    argv[0]);
> +            return EXIT_FAILURE;
> +        }
> +    }
> +
> +    if (daemonize) {
> +        g_debug("starting daemon");
> +        become_daemon(pidfile);
> +    }
> +
> +    g_type_init();
> +    g_thread_init(NULL);
> +
> +    s = qemu_mallocz(sizeof(GAState));
> +    s->conn_id = 0;
> +    s->conn_channel = NULL;
> +    s->path = path;
> +    s->method = method;
> +    s->log_file = log_file;
> +    s->log_level = log_level;
> +    g_log_set_default_handler(ga_log, s);
> +    g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
> +    s->logging_enabled = true;
> +    ga_state = s;
> +
> +    module_call_init(MODULE_INIT_QAPI);
> +    init_guest_agent(ga_state);
> +    register_signal_handlers();
> +
> +    g_main_loop_run(ga_state->main_loop);
> +
> +    unlink(pidfile);
> +
> +    return 0;
> +}
> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
> index 688f120..66d1729 100644
> --- a/qga/guest-agent-core.h
> +++ b/qga/guest-agent-core.h
> @@ -15,6 +15,7 @@
>  
>  #define QGA_VERSION "1.0"
>  
> +typedef struct GAState GAState;
>  typedef struct GACommandState GACommandState;
>  
>  void ga_command_state_add(GACommandState *cs,
> @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
>  void ga_command_state_init_all(GACommandState *cs);
>  void ga_command_state_cleanup_all(GACommandState *cs);
>  GACommandState *ga_command_state_new(void);
> +bool ga_logging_enabled(GAState *s);
> +void ga_disable_logging(GAState *s);
> +void ga_enable_logging(GAState *s);

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

* Re: [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-20 17:56   ` Luiz Capitulino
@ 2011-07-20 19:25     ` Michael Roth
  2011-07-20 19:31       ` Luiz Capitulino
  0 siblings, 1 reply; 30+ messages in thread
From: Michael Roth @ 2011-07-20 19:25 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: aliguori, agl, qemu-devel, Jes.Sorensen

On 07/20/2011 12:56 PM, Luiz Capitulino wrote:
> On Tue, 19 Jul 2011 15:41:54 -0500
> Michael Roth<mdroth@linux.vnet.ibm.com>  wrote:
>
>> This is the actual guest daemon, it listens for requests over a
>> virtio-serial/isa-serial/unix socket channel and routes them through
>> to dispatch routines, and writes the results back to the channel in
>> a manner similar to QMP.
>>
>> A shorthand invocation:
>>
>>    qemu-ga -d
>>
>> Is equivalent to:
>>
>>    qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>            -f /var/run/qemu-ga.pid -d
>>
>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>> ---
>>   Makefile               |    8 +-
>>   configure              |    1 +
>>   qemu-ga.c              |  667 ++++++++++++++++++++++++++++++++++++++++++++++++
>>   qga/guest-agent-core.h |    4 +
>>   4 files changed, 677 insertions(+), 3 deletions(-)
>>   create mode 100644 qemu-ga.c
>>
>> diff --git a/Makefile b/Makefile
>> index b8cdf0e..0d2e33d 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -140,7 +140,7 @@ endif
>>   ######################################################################
>>
>>   qemu-img.o: qemu-img-cmds.h
>> -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
>> +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
>>
>>   qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
>>
>> @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o
>>
>>   QGALIB=qga/guest-agent-command-state.o
>>
>> +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
>> +
>>   QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
>>
>>   clean:
>>   # avoid old build problems by removing potentially incorrect old files
>>   	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
>>   	rm -f qemu-options.def
>> -	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
>> +	rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
>>   	rm -Rf .libs
>>   	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
>>   	rm -f qemu-img-cmds.h
>> @@ -386,4 +388,4 @@ tarbin:
>>   	$(mandir)/man8/qemu-nbd.8
>>
>>   # Include automatically generated dependency files
>> --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d)
>> +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
>> diff --git a/configure b/configure
>> index 6a03002..445f674 100755
>> --- a/configure
>> +++ b/configure
>> @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
>>     tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
>>     if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
>>         tools="qemu-nbd\$(EXESUF) $tools"
>> +      tools="qemu-ga\$(EXESUF) $tools"
>>       if [ "$check_utests" = "yes" ]; then
>>         tools="check-qint check-qstring check-qdict check-qlist $tools"
>>         tools="check-qfloat check-qjson $tools"
>> diff --git a/qemu-ga.c b/qemu-ga.c
>> new file mode 100644
>> index 0000000..eb09100
>> --- /dev/null
>> +++ b/qemu-ga.c
>> @@ -0,0 +1,667 @@
>> +/*
>> + * QEMU Guest Agent
>> + *
>> + * Copyright IBM Corp. 2011
>> + *
>> + * Authors:
>> + *  Adam Litke<aglitke@linux.vnet.ibm.com>
>> + *  Michael Roth<mdroth@linux.vnet.ibm.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<stdlib.h>
>> +#include<stdio.h>
>> +#include<stdbool.h>
>> +#include<glib.h>
>> +#include<gio/gio.h>
>> +#include<getopt.h>
>> +#include<termios.h>
>> +#include<syslog.h>
>> +#include "qemu_socket.h"
>> +#include "json-streamer.h"
>> +#include "json-parser.h"
>> +#include "qint.h"
>> +#include "qjson.h"
>> +#include "qga/guest-agent-core.h"
>> +#include "module.h"
>> +#include "signal.h"
>> +#include "qerror.h"
>> +#include "error_int.h"
>> +
>> +#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
>> +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
>> +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
>> +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
>> +
>> +struct GAState {
>> +    JSONMessageParser parser;
>> +    GMainLoop *main_loop;
>> +    guint conn_id;
>> +    GSocket *conn_sock;
>> +    GIOChannel *conn_channel;
>> +    guint listen_id;
>> +    GSocket *listen_sock;
>> +    GIOChannel *listen_channel;
>> +    const char *path;
>> +    const char *method;
>> +    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
>> +    GACommandState *command_state;
>> +    GLogLevelFlags log_level;
>> +    FILE *log_file;
>> +    bool logging_enabled;
>> +};
>> +
>> +static struct GAState *ga_state;
>> +
>> +static void quit_handler(int sig)
>> +{
>> +    g_debug("recieved signal num %d, quitting", sig);
>> +
>> +    if (g_main_loop_is_running(ga_state->main_loop)) {
>> +        g_main_loop_quit(ga_state->main_loop);
>> +    }
>> +}
>> +
>> +static void register_signal_handlers(void)
>> +{
>> +    struct sigaction sigact;
>> +    int ret;
>> +
>> +    memset(&sigact, 0, sizeof(struct sigaction));
>> +    sigact.sa_handler = quit_handler;
>> +
>> +    ret = sigaction(SIGINT,&sigact, NULL);
>> +    if (ret == -1) {
>> +        g_error("error configuring signal handler: %s", strerror(errno));
>> +        exit(EXIT_FAILURE);
>> +    }
>> +    ret = sigaction(SIGTERM,&sigact, NULL);
>> +    if (ret == -1) {
>> +        g_error("error configuring signal handler: %s", strerror(errno));
>> +    }
>> +}
>> +
>> +static void usage(const char *cmd)
>> +{
>> +    printf(
>> +"Usage: %s -c<channel_opts>\n"
>> +"QEMU Guest Agent %s\n"
>> +"\n"
>> +"  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
>> +"                    isa-serial (virtio-serial is the default)\n"
>> +"  -p, --path        device/socket path (%s is the default for virtio-serial)\n"
>> +"  -l, --logfile     set logfile path, logs to stderr by default\n"
>> +"  -f, --pidfile     specify pidfile (default is %s)\n"
>> +"  -v, --verbose     log extra debugging information\n"
>> +"  -V, --version     print version information and exit\n"
>> +"  -d, --daemonize   become a daemon\n"
>> +"  -h, --help        display this help and exit\n"
>> +"\n"
>> +"Report bugs to<mdroth@linux.vnet.ibm.com>\n"
>> +    , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
>> +}
>> +
>> +static void conn_channel_close(GAState *s);
>> +
>> +static const char *ga_log_level_str(GLogLevelFlags level)
>> +{
>> +    switch (level&  G_LOG_LEVEL_MASK) {
>> +        case G_LOG_LEVEL_ERROR:
>> +            return "error";
>> +        case G_LOG_LEVEL_CRITICAL:
>> +            return "critical";
>> +        case G_LOG_LEVEL_WARNING:
>> +            return "warning";
>> +        case G_LOG_LEVEL_MESSAGE:
>> +            return "message";
>> +        case G_LOG_LEVEL_INFO:
>> +            return "info";
>> +        case G_LOG_LEVEL_DEBUG:
>> +            return "debug";
>> +        default:
>> +            return "user";
>> +    }
>> +}
>> +
>> +bool ga_logging_enabled(GAState *s)
>> +{
>> +    return s->logging_enabled;
>> +}
>> +
>> +void ga_disable_logging(GAState *s)
>> +{
>> +    s->logging_enabled = false;
>> +}
>> +
>> +void ga_enable_logging(GAState *s)
>> +{
>> +    s->logging_enabled = true;
>> +}
>> +
>> +static void ga_log(const gchar *domain, GLogLevelFlags level,
>> +                   const gchar *msg, gpointer opaque)
>> +{
>> +    GAState *s = opaque;
>> +    GTimeVal time;
>> +    const char *level_str = ga_log_level_str(level);
>> +
>> +    if (!ga_logging_enabled(s)) {
>> +        return;
>> +    }
>> +
>> +    level&= G_LOG_LEVEL_MASK;
>> +    if (g_strcmp0(domain, "syslog") == 0) {
>> +        syslog(LOG_INFO, "%s: %s", level_str, msg);
>> +    } else if (level&  s->log_level) {
>> +        g_get_current_time(&time);
>> +        fprintf(s->log_file,
>> +                "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
>> +        fflush(s->log_file);
>> +    }
>> +}
>> +
>> +static void become_daemon(const char *pidfile)
>> +{
>> +    pid_t pid, sid;
>> +    int pidfd;
>> +    char *pidstr = NULL;
>> +
>> +    pid = fork();
>> +    if (pid<  0) {
>> +        exit(EXIT_FAILURE);
>> +    }
>> +    if (pid>  0) {
>> +        exit(EXIT_SUCCESS);
>> +    }
>> +
>> +    pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
>> +    if (pidfd == -1) {
>> +        g_critical("Cannot create pid file, %s", strerror(errno));
>> +        exit(EXIT_FAILURE);
>> +    }
>> +
>> +    if (asprintf(&pidstr, "%d", getpid()) == -1) {
>> +        g_critical("Cannot allocate memory");
>> +        goto fail;
>> +    }
>> +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
>> +        free(pidstr);
>> +        g_critical("Failed to write pid file");
>> +        goto fail;
>> +    }
>> +
>> +    umask(0);
>> +    sid = setsid();
>> +    if (sid<  0) {
>> +        goto fail;
>> +    }
>> +    if ((chdir("/"))<  0) {
>> +        goto fail;
>> +    }
>> +
>> +    close(STDIN_FILENO);
>> +    close(STDOUT_FILENO);
>> +    close(STDERR_FILENO);
>> +    free(pidstr);
>> +    return;
>> +
>> +fail:
>> +    unlink(pidfile);
>> +    g_critical("failed to daemonize");
>> +    exit(EXIT_FAILURE);
>> +}
>> +
>> +static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
>> +                                 gsize count)
>> +{
>> +    GError *err = NULL;
>> +    gsize written = 0;
>> +    GIOStatus status;
>> +
>> +    while (count) {
>> +        status = g_io_channel_write_chars(channel, buf, count,&written,&err);
>> +        g_debug("sending data, count: %d", (int)count);
>> +        if (err != NULL) {
>> +            g_warning("error sending newline: %s", err->message);
>> +            return err->code;
>> +        }
>> +        if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
>> +            return -EPIPE;
>> +        }
>> +
>> +        if (status == G_IO_STATUS_NORMAL) {
>> +            count -= written;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
>> +{
>> +    int ret = 0;
>> +    const char *buf;
>> +    QString *payload_qstr;
>> +    GError *err = NULL;
>> +
>> +    g_assert(payload&&  channel);
>> +
>> +    payload_qstr = qobject_to_json(payload);
>> +    if (!payload_qstr) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    qstring_append_chr(payload_qstr, '\n');
>> +    buf = qstring_get_str(payload_qstr);
>> +    ret = conn_channel_send_buf(channel, buf, strlen(buf));
>> +    if (ret) {
>> +        goto out_free;
>> +    }
>> +
>> +    g_io_channel_flush(channel,&err);
>> +    if (err != NULL) {
>> +        g_warning("error flushing payload: %s", err->message);
>> +        ret = err->code;
>> +        goto out_free;
>> +    }
>> +
>> +out_free:
>> +    QDECREF(payload_qstr);
>> +    if (err) {
>> +        g_error_free(err);
>> +    }
>> +    return ret;
>> +}
>> +
>> +static void process_command(GAState *s, QDict *req)
>> +{
>> +    QObject *rsp = NULL;
>> +    int ret;
>> +
>> +    g_assert(req);
>> +    g_debug("processing command");
>> +    rsp = qmp_dispatch(QOBJECT(req));
>> +    if (rsp) {
>> +        ret = conn_channel_send_payload(s->conn_channel, rsp);
>> +        if (ret) {
>> +            g_warning("error sending payload: %s", strerror(ret));
>> +        }
>> +        qobject_decref(rsp);
>> +    } else {
>> +        g_warning("error getting response");
>> +    }
>> +}
>> +
>> +/* handle requests/control events coming in over the channel */
>> +static void process_event(JSONMessageParser *parser, QList *tokens)
>> +{
>> +    GAState *s = container_of(parser, GAState, parser);
>> +    QObject *obj;
>> +    QDict *qdict;
>> +    Error *err = NULL;
>> +    int ret;
>> +
>> +    g_assert(s&&  parser);
>> +
>> +    g_debug("process_event: called");
>> +    obj = json_parser_parse_err(tokens, NULL,&err);
>> +    if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
>> +        qobject_decref(obj);
>> +        qdict = qdict_new();
>> +        if (!err) {
>> +            g_warning("failed to parse event: unknown error");
>> +            error_set(&err, QERR_JSON_PARSING);
>> +        } else {
>> +            g_warning("failed to parse event: %s", error_get_pretty(err));
>> +        }
>> +        qdict_put_obj(qdict, "error", error_get_qobject(err));
>> +        error_free(err);
>> +    } else {
>> +        qdict = qobject_to_qdict(obj);
>> +    }
>> +
>> +    g_assert(qdict);
>> +
>> +    /* handle host->guest commands */
>> +    if (qdict_haskey(qdict, "execute")) {
>> +        process_command(s, qdict);
>> +    } else {
>> +        if (!qdict_haskey(qdict, "error")) {
>> +            QDECREF(qdict);
>> +            qdict = qdict_new();
>> +            g_warning("unrecognized payload format");
>> +            error_set(&err, QERR_UNSUPPORTED);
>> +            qdict_put_obj(qdict, "error", error_get_qobject(err));
>> +            error_free(err);
>> +        }
>> +        ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
>> +        if (ret) {
>> +            g_warning("error sending payload: %s", strerror(ret));
>> +        }
>> +    }
>> +
>> +    QDECREF(qdict);
>> +}
>> +
>> +static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
>> +                                  gpointer data)
>> +{
>> +    GAState *s = data;
>> +    gchar buf[1024];
>> +    gsize count;
>> +    GError *err = NULL;
>> +    memset(buf, 0, 1024);
>> +    GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
>> +&count,&err);
>> +    if (err != NULL) {
>> +        g_warning("error reading channel: %s", err->message);
>> +        conn_channel_close(s);
>> +        g_error_free(err);
>> +        return false;
>> +    }
>> +    switch (status) {
>> +    case G_IO_STATUS_ERROR:
>> +        g_warning("problem");
>> +        return false;
>> +    case G_IO_STATUS_NORMAL:
>> +        g_debug("read data, count: %d, data: %s", (int)count, buf);
>> +        json_message_parser_feed(&s->parser, (char *)buf, (int)count);
>> +    case G_IO_STATUS_AGAIN:
>> +        /* virtio causes us to spin here when no process is attached to
>> +         * host-side chardev. sleep a bit to mitigate this
>> +         */
>> +        if (s->virtio) {
>> +            usleep(100*1000);
>> +        }
>> +        return true;
>> +    case G_IO_STATUS_EOF:
>> +        g_debug("received EOF");
>> +        conn_channel_close(s);
>> +        if (s->virtio) {
>> +            return true;
>> +        }
>> +        return false;
>> +    default:
>> +        g_warning("unknown channel read status, closing");
>> +        conn_channel_close(s);
>> +        return false;
>> +    }
>> +    return true;
>> +}
>> +
>> +static int conn_channel_add(GAState *s, int fd)
>> +{
>> +    GIOChannel *conn_channel;
>> +    guint conn_id;
>> +    GError *err = NULL;
>> +
>> +    g_assert(s&&  !s->conn_channel);
>> +    conn_channel = g_io_channel_unix_new(fd);
>> +    g_assert(conn_channel);
>> +    g_io_channel_set_encoding(conn_channel, NULL,&err);
>> +    if (err != NULL) {
>> +        g_warning("error setting channel encoding to binary");
>> +        g_error_free(err);
>> +        return -1;
>> +    }
>> +    conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
>> +                             conn_channel_read, s);
>> +    if (err != NULL) {
>> +        g_warning("error adding io watch: %s", err->message);
>> +        g_error_free(err);
>> +        return -1;
>> +    }
>> +    s->conn_channel = conn_channel;
>> +    s->conn_id = conn_id;
>> +    return 0;
>> +}
>> +
>> +static gboolean listen_channel_accept(GIOChannel *channel,
>> +                                      GIOCondition condition, gpointer data)
>> +{
>> +    GAState *s = data;
>> +    GError *err = NULL;
>> +    g_assert(channel != NULL);
>> +    int ret;
>> +    bool accepted = false;
>> +
>> +    s->conn_sock = g_socket_accept(s->listen_sock, NULL,&err);
>> +    if (err != NULL) {
>> +        g_warning("error converting fd to gsocket: %s", err->message);
>> +        g_error_free(err);
>> +        goto out;
>> +    }
>> +    ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
>> +    if (ret) {
>> +        g_warning("error setting up connection");
>> +        goto out;
>> +    }
>> +    accepted = true;
>> +
>> +out:
>> +    /* only accept 1 connection at a time */
>> +    return !accepted;
>> +}
>> +
>> +/* start polling for readable events on listen fd, new==true
>> + * indicates we should use the existing s->listen_channel
>> + */
>> +static int listen_channel_add(GAState *s, int listen_fd, bool new)
>> +{
>> +    GError *err = NULL;
>> +    guint listen_id;
>> +
>> +    if (new) {
>> +        s->listen_channel = g_io_channel_unix_new(listen_fd);
>> +        if (s->listen_sock) {
>> +            g_object_unref(s->listen_sock);
>> +        }
>> +        s->listen_sock = g_socket_new_from_fd(listen_fd,&err);
>> +        if (err != NULL) {
>> +            g_warning("error converting fd to gsocket: %s", err->message);
>> +            g_error_free(err);
>> +            return -1;
>> +        }
>> +    }
>> +    listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
>> +                               listen_channel_accept, s);
>> +    if (err != NULL) {
>> +        g_warning("error adding io watch: %s", err->message);
>> +        g_error_free(err);
>> +        return -1;
>> +    }
>> +    return 0;
>
> I'm getting the following error when building this on a F15 guest:
>
> qemu-ga.c: In function ‘listen_channel_add’:
> qemu-ga.c:453:11: error: variable ‘listen_id’ set but not used [-Werror=unused-but-set-variable]
> cc1: all warnings being treated as errors
>
> g_io_add_watch() doesn't seem to return an error. So if the fix is just to
> drop 'listen_id' and the last if statement altogether I can do it myself,
> otherwise you can send me only this patch.
>
> The series looks good otherwise.
>

Doh, sorry for not catching this. I did the same thing with conn_id as 
well. I made a patch available here:

http://repo.or.cz/w/qemu/mdroth.git/blobdiff/a8d38e5bb2d558918ac0bad1ee618472ae34fe7a..581922e148f37e8f5844eeb11257eef8e9302709:/qemu-ga.c

I tested the patch on a 32-bit F15 guest and it seems to do the trick.

If you'd like you can just pull from

git://repo.or.cz/qemu/mdroth.git qga-dev

Which is this series (set3-v8) plus the patch right now.

>> +}
>> +
>> +/* cleanup state for closed connection/session, start accepting new
>> + * connections if we're in listening mode
>> + */
>> +static void conn_channel_close(GAState *s)
>> +{
>> +    if (strcmp(s->method, "unix-listen") == 0) {
>> +        g_io_channel_shutdown(s->conn_channel, true, NULL);
>> +        g_object_unref(s->conn_sock);
>> +        s->conn_sock = NULL;
>> +        listen_channel_add(s, 0, false);
>> +    } else if (strcmp(s->method, "virtio-serial") == 0) {
>> +        /* we spin on EOF for virtio-serial, so back off a bit. also,
>> +         * dont close the connection in this case, it'll resume normal
>> +         * operation when another process connects to host chardev
>> +         */
>> +        usleep(100*1000);
>> +        goto out_noclose;
>> +    }
>> +    g_io_channel_unref(s->conn_channel);
>> +    s->conn_channel = NULL;
>> +    s->conn_id = 0;
>> +out_noclose:
>> +    return;
>> +}
>> +
>> +static void init_guest_agent(GAState *s)
>> +{
>> +    struct termios tio;
>> +    int ret, fd;
>> +
>> +    if (s->method == NULL) {
>> +        /* try virtio-serial as our default */
>> +        s->method = "virtio-serial";
>> +    }
>> +
>> +    if (s->path == NULL) {
>> +        if (strcmp(s->method, "virtio-serial") != 0) {
>> +            g_critical("must specify a path for this channel");
>> +            exit(EXIT_FAILURE);
>> +        }
>> +        /* try the default path for the virtio-serial port */
>> +        s->path = QGA_VIRTIO_PATH_DEFAULT;
>> +    }
>> +
>> +    if (strcmp(s->method, "virtio-serial") == 0) {
>> +        s->virtio = true;
>> +        fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
>> +        if (fd == -1) {
>> +            g_critical("error opening channel: %s", strerror(errno));
>> +            exit(EXIT_FAILURE);
>> +        }
>> +        ret = conn_channel_add(s, fd);
>> +        if (ret) {
>> +            g_critical("error adding channel to main loop");
>> +            exit(EXIT_FAILURE);
>> +        }
>> +    } else if (strcmp(s->method, "isa-serial") == 0) {
>> +        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
>> +        if (fd == -1) {
>> +            g_critical("error opening channel: %s", strerror(errno));
>> +            exit(EXIT_FAILURE);
>> +        }
>> +        tcgetattr(fd,&tio);
>> +        /* set up serial port for non-canonical, dumb byte streaming */
>> +        tio.c_iflag&= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
>> +                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
>> +                         IMAXBEL);
>> +        tio.c_oflag = 0;
>> +        tio.c_lflag = 0;
>> +        tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
>> +        /* 1 available byte min or reads will block (we'll set non-blocking
>> +         * elsewhere, else we have to deal with read()=0 instead)
>> +         */
>> +        tio.c_cc[VMIN] = 1;
>> +        tio.c_cc[VTIME] = 0;
>> +        /* flush everything waiting for read/xmit, it's garbage at this point */
>> +        tcflush(fd, TCIFLUSH);
>> +        tcsetattr(fd, TCSANOW,&tio);
>> +        ret = conn_channel_add(s, fd);
>> +        if (ret) {
>> +            g_error("error adding channel to main loop");
>> +        }
>> +    } else if (strcmp(s->method, "unix-listen") == 0) {
>> +        fd = unix_listen(s->path, NULL, strlen(s->path));
>> +        if (fd == -1) {
>> +            g_critical("error opening path: %s", strerror(errno));
>> +            exit(EXIT_FAILURE);
>> +        }
>> +        ret = listen_channel_add(s, fd, true);
>> +        if (ret) {
>> +            g_critical("error binding/listening to specified socket");
>> +            exit(EXIT_FAILURE);
>> +        }
>> +    } else {
>> +        g_critical("unsupported channel method/type: %s", s->method);
>> +        exit(EXIT_FAILURE);
>> +    }
>> +
>> +    json_message_parser_init(&s->parser, process_event);
>> +    s->main_loop = g_main_loop_new(NULL, false);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    const char *sopt = "hVvdm:p:l:f:";
>> +    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
>> +    const struct option lopt[] = {
>> +        { "help", 0, NULL, 'h' },
>> +        { "version", 0, NULL, 'V' },
>> +        { "logfile", 0, NULL, 'l' },
>> +        { "pidfile", 0, NULL, 'f' },
>> +        { "verbose", 0, NULL, 'v' },
>> +        { "method", 0, NULL, 'm' },
>> +        { "path", 0, NULL, 'p' },
>> +        { "daemonize", 0, NULL, 'd' },
>> +        { NULL, 0, NULL, 0 }
>> +    };
>> +    int opt_ind = 0, ch, daemonize = 0;
>> +    GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
>> +    FILE *log_file = stderr;
>> +    GAState *s;
>> +
>> +    while ((ch = getopt_long(argc, argv, sopt, lopt,&opt_ind)) != -1) {
>> +        switch (ch) {
>> +        case 'm':
>> +            method = optarg;
>> +            break;
>> +        case 'p':
>> +            path = optarg;
>> +            break;
>> +        case 'l':
>> +            log_file = fopen(optarg, "a");
>> +            if (!log_file) {
>> +                g_critical("unable to open specified log file: %s",
>> +                           strerror(errno));
>> +                return EXIT_FAILURE;
>> +            }
>> +            break;
>> +        case 'f':
>> +            pidfile = optarg;
>> +            break;
>> +        case 'v':
>> +            /* enable all log levels */
>> +            log_level = G_LOG_LEVEL_MASK;
>> +            break;
>> +        case 'V':
>> +            printf("QEMU Guest Agent %s\n", QGA_VERSION);
>> +            return 0;
>> +        case 'd':
>> +            daemonize = 1;
>> +            break;
>> +        case 'h':
>> +            usage(argv[0]);
>> +            return 0;
>> +        case '?':
>> +            g_print("Unknown option, try '%s --help' for more information.\n",
>> +                    argv[0]);
>> +            return EXIT_FAILURE;
>> +        }
>> +    }
>> +
>> +    if (daemonize) {
>> +        g_debug("starting daemon");
>> +        become_daemon(pidfile);
>> +    }
>> +
>> +    g_type_init();
>> +    g_thread_init(NULL);
>> +
>> +    s = qemu_mallocz(sizeof(GAState));
>> +    s->conn_id = 0;
>> +    s->conn_channel = NULL;
>> +    s->path = path;
>> +    s->method = method;
>> +    s->log_file = log_file;
>> +    s->log_level = log_level;
>> +    g_log_set_default_handler(ga_log, s);
>> +    g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
>> +    s->logging_enabled = true;
>> +    ga_state = s;
>> +
>> +    module_call_init(MODULE_INIT_QAPI);
>> +    init_guest_agent(ga_state);
>> +    register_signal_handlers();
>> +
>> +    g_main_loop_run(ga_state->main_loop);
>> +
>> +    unlink(pidfile);
>> +
>> +    return 0;
>> +}
>> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
>> index 688f120..66d1729 100644
>> --- a/qga/guest-agent-core.h
>> +++ b/qga/guest-agent-core.h
>> @@ -15,6 +15,7 @@
>>
>>   #define QGA_VERSION "1.0"
>>
>> +typedef struct GAState GAState;
>>   typedef struct GACommandState GACommandState;
>>
>>   void ga_command_state_add(GACommandState *cs,
>> @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
>>   void ga_command_state_init_all(GACommandState *cs);
>>   void ga_command_state_cleanup_all(GACommandState *cs);
>>   GACommandState *ga_command_state_new(void);
>> +bool ga_logging_enabled(GAState *s);
>> +void ga_disable_logging(GAState *s);
>> +void ga_enable_logging(GAState *s);
>

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

* Re: [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-20 19:25     ` Michael Roth
@ 2011-07-20 19:31       ` Luiz Capitulino
  2011-07-20 20:01         ` Michael Roth
  0 siblings, 1 reply; 30+ messages in thread
From: Luiz Capitulino @ 2011-07-20 19:31 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, agl, qemu-devel, Jes.Sorensen

On Wed, 20 Jul 2011 14:25:06 -0500
Michael Roth <mdroth@linux.vnet.ibm.com> wrote:

> On 07/20/2011 12:56 PM, Luiz Capitulino wrote:
> > On Tue, 19 Jul 2011 15:41:54 -0500
> > Michael Roth<mdroth@linux.vnet.ibm.com>  wrote:
> >
> >> This is the actual guest daemon, it listens for requests over a
> >> virtio-serial/isa-serial/unix socket channel and routes them through
> >> to dispatch routines, and writes the results back to the channel in
> >> a manner similar to QMP.
> >>
> >> A shorthand invocation:
> >>
> >>    qemu-ga -d
> >>
> >> Is equivalent to:
> >>
> >>    qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
> >>            -f /var/run/qemu-ga.pid -d
> >>
> >> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
> >> ---
> >>   Makefile               |    8 +-
> >>   configure              |    1 +
> >>   qemu-ga.c              |  667 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>   qga/guest-agent-core.h |    4 +
> >>   4 files changed, 677 insertions(+), 3 deletions(-)
> >>   create mode 100644 qemu-ga.c
> >>
> >> diff --git a/Makefile b/Makefile
> >> index b8cdf0e..0d2e33d 100644
> >> --- a/Makefile
> >> +++ b/Makefile
> >> @@ -140,7 +140,7 @@ endif
> >>   ######################################################################
> >>
> >>   qemu-img.o: qemu-img-cmds.h
> >> -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
> >> +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
> >>
> >>   qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
> >>
> >> @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o
> >>
> >>   QGALIB=qga/guest-agent-command-state.o
> >>
> >> +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
> >> +
> >>   QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
> >>
> >>   clean:
> >>   # avoid old build problems by removing potentially incorrect old files
> >>   	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
> >>   	rm -f qemu-options.def
> >> -	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
> >> +	rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
> >>   	rm -Rf .libs
> >>   	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
> >>   	rm -f qemu-img-cmds.h
> >> @@ -386,4 +388,4 @@ tarbin:
> >>   	$(mandir)/man8/qemu-nbd.8
> >>
> >>   # Include automatically generated dependency files
> >> --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d)
> >> +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
> >> diff --git a/configure b/configure
> >> index 6a03002..445f674 100755
> >> --- a/configure
> >> +++ b/configure
> >> @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
> >>     tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
> >>     if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
> >>         tools="qemu-nbd\$(EXESUF) $tools"
> >> +      tools="qemu-ga\$(EXESUF) $tools"
> >>       if [ "$check_utests" = "yes" ]; then
> >>         tools="check-qint check-qstring check-qdict check-qlist $tools"
> >>         tools="check-qfloat check-qjson $tools"
> >> diff --git a/qemu-ga.c b/qemu-ga.c
> >> new file mode 100644
> >> index 0000000..eb09100
> >> --- /dev/null
> >> +++ b/qemu-ga.c
> >> @@ -0,0 +1,667 @@
> >> +/*
> >> + * QEMU Guest Agent
> >> + *
> >> + * Copyright IBM Corp. 2011
> >> + *
> >> + * Authors:
> >> + *  Adam Litke<aglitke@linux.vnet.ibm.com>
> >> + *  Michael Roth<mdroth@linux.vnet.ibm.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<stdlib.h>
> >> +#include<stdio.h>
> >> +#include<stdbool.h>
> >> +#include<glib.h>
> >> +#include<gio/gio.h>
> >> +#include<getopt.h>
> >> +#include<termios.h>
> >> +#include<syslog.h>
> >> +#include "qemu_socket.h"
> >> +#include "json-streamer.h"
> >> +#include "json-parser.h"
> >> +#include "qint.h"
> >> +#include "qjson.h"
> >> +#include "qga/guest-agent-core.h"
> >> +#include "module.h"
> >> +#include "signal.h"
> >> +#include "qerror.h"
> >> +#include "error_int.h"
> >> +
> >> +#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
> >> +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
> >> +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
> >> +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
> >> +
> >> +struct GAState {
> >> +    JSONMessageParser parser;
> >> +    GMainLoop *main_loop;
> >> +    guint conn_id;
> >> +    GSocket *conn_sock;
> >> +    GIOChannel *conn_channel;
> >> +    guint listen_id;
> >> +    GSocket *listen_sock;
> >> +    GIOChannel *listen_channel;
> >> +    const char *path;
> >> +    const char *method;
> >> +    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
> >> +    GACommandState *command_state;
> >> +    GLogLevelFlags log_level;
> >> +    FILE *log_file;
> >> +    bool logging_enabled;
> >> +};
> >> +
> >> +static struct GAState *ga_state;
> >> +
> >> +static void quit_handler(int sig)
> >> +{
> >> +    g_debug("recieved signal num %d, quitting", sig);
> >> +
> >> +    if (g_main_loop_is_running(ga_state->main_loop)) {
> >> +        g_main_loop_quit(ga_state->main_loop);
> >> +    }
> >> +}
> >> +
> >> +static void register_signal_handlers(void)
> >> +{
> >> +    struct sigaction sigact;
> >> +    int ret;
> >> +
> >> +    memset(&sigact, 0, sizeof(struct sigaction));
> >> +    sigact.sa_handler = quit_handler;
> >> +
> >> +    ret = sigaction(SIGINT,&sigact, NULL);
> >> +    if (ret == -1) {
> >> +        g_error("error configuring signal handler: %s", strerror(errno));
> >> +        exit(EXIT_FAILURE);
> >> +    }
> >> +    ret = sigaction(SIGTERM,&sigact, NULL);
> >> +    if (ret == -1) {
> >> +        g_error("error configuring signal handler: %s", strerror(errno));
> >> +    }
> >> +}
> >> +
> >> +static void usage(const char *cmd)
> >> +{
> >> +    printf(
> >> +"Usage: %s -c<channel_opts>\n"
> >> +"QEMU Guest Agent %s\n"
> >> +"\n"
> >> +"  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
> >> +"                    isa-serial (virtio-serial is the default)\n"
> >> +"  -p, --path        device/socket path (%s is the default for virtio-serial)\n"
> >> +"  -l, --logfile     set logfile path, logs to stderr by default\n"
> >> +"  -f, --pidfile     specify pidfile (default is %s)\n"
> >> +"  -v, --verbose     log extra debugging information\n"
> >> +"  -V, --version     print version information and exit\n"
> >> +"  -d, --daemonize   become a daemon\n"
> >> +"  -h, --help        display this help and exit\n"
> >> +"\n"
> >> +"Report bugs to<mdroth@linux.vnet.ibm.com>\n"
> >> +    , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
> >> +}
> >> +
> >> +static void conn_channel_close(GAState *s);
> >> +
> >> +static const char *ga_log_level_str(GLogLevelFlags level)
> >> +{
> >> +    switch (level&  G_LOG_LEVEL_MASK) {
> >> +        case G_LOG_LEVEL_ERROR:
> >> +            return "error";
> >> +        case G_LOG_LEVEL_CRITICAL:
> >> +            return "critical";
> >> +        case G_LOG_LEVEL_WARNING:
> >> +            return "warning";
> >> +        case G_LOG_LEVEL_MESSAGE:
> >> +            return "message";
> >> +        case G_LOG_LEVEL_INFO:
> >> +            return "info";
> >> +        case G_LOG_LEVEL_DEBUG:
> >> +            return "debug";
> >> +        default:
> >> +            return "user";
> >> +    }
> >> +}
> >> +
> >> +bool ga_logging_enabled(GAState *s)
> >> +{
> >> +    return s->logging_enabled;
> >> +}
> >> +
> >> +void ga_disable_logging(GAState *s)
> >> +{
> >> +    s->logging_enabled = false;
> >> +}
> >> +
> >> +void ga_enable_logging(GAState *s)
> >> +{
> >> +    s->logging_enabled = true;
> >> +}
> >> +
> >> +static void ga_log(const gchar *domain, GLogLevelFlags level,
> >> +                   const gchar *msg, gpointer opaque)
> >> +{
> >> +    GAState *s = opaque;
> >> +    GTimeVal time;
> >> +    const char *level_str = ga_log_level_str(level);
> >> +
> >> +    if (!ga_logging_enabled(s)) {
> >> +        return;
> >> +    }
> >> +
> >> +    level&= G_LOG_LEVEL_MASK;
> >> +    if (g_strcmp0(domain, "syslog") == 0) {
> >> +        syslog(LOG_INFO, "%s: %s", level_str, msg);
> >> +    } else if (level&  s->log_level) {
> >> +        g_get_current_time(&time);
> >> +        fprintf(s->log_file,
> >> +                "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
> >> +        fflush(s->log_file);
> >> +    }
> >> +}
> >> +
> >> +static void become_daemon(const char *pidfile)
> >> +{
> >> +    pid_t pid, sid;
> >> +    int pidfd;
> >> +    char *pidstr = NULL;
> >> +
> >> +    pid = fork();
> >> +    if (pid<  0) {
> >> +        exit(EXIT_FAILURE);
> >> +    }
> >> +    if (pid>  0) {
> >> +        exit(EXIT_SUCCESS);
> >> +    }
> >> +
> >> +    pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
> >> +    if (pidfd == -1) {
> >> +        g_critical("Cannot create pid file, %s", strerror(errno));
> >> +        exit(EXIT_FAILURE);
> >> +    }
> >> +
> >> +    if (asprintf(&pidstr, "%d", getpid()) == -1) {
> >> +        g_critical("Cannot allocate memory");
> >> +        goto fail;
> >> +    }
> >> +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
> >> +        free(pidstr);
> >> +        g_critical("Failed to write pid file");
> >> +        goto fail;
> >> +    }
> >> +
> >> +    umask(0);
> >> +    sid = setsid();
> >> +    if (sid<  0) {
> >> +        goto fail;
> >> +    }
> >> +    if ((chdir("/"))<  0) {
> >> +        goto fail;
> >> +    }
> >> +
> >> +    close(STDIN_FILENO);
> >> +    close(STDOUT_FILENO);
> >> +    close(STDERR_FILENO);
> >> +    free(pidstr);
> >> +    return;
> >> +
> >> +fail:
> >> +    unlink(pidfile);
> >> +    g_critical("failed to daemonize");
> >> +    exit(EXIT_FAILURE);
> >> +}
> >> +
> >> +static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
> >> +                                 gsize count)
> >> +{
> >> +    GError *err = NULL;
> >> +    gsize written = 0;
> >> +    GIOStatus status;
> >> +
> >> +    while (count) {
> >> +        status = g_io_channel_write_chars(channel, buf, count,&written,&err);
> >> +        g_debug("sending data, count: %d", (int)count);
> >> +        if (err != NULL) {
> >> +            g_warning("error sending newline: %s", err->message);
> >> +            return err->code;
> >> +        }
> >> +        if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
> >> +            return -EPIPE;
> >> +        }
> >> +
> >> +        if (status == G_IO_STATUS_NORMAL) {
> >> +            count -= written;
> >> +        }
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
> >> +{
> >> +    int ret = 0;
> >> +    const char *buf;
> >> +    QString *payload_qstr;
> >> +    GError *err = NULL;
> >> +
> >> +    g_assert(payload&&  channel);
> >> +
> >> +    payload_qstr = qobject_to_json(payload);
> >> +    if (!payload_qstr) {
> >> +        return -EINVAL;
> >> +    }
> >> +
> >> +    qstring_append_chr(payload_qstr, '\n');
> >> +    buf = qstring_get_str(payload_qstr);
> >> +    ret = conn_channel_send_buf(channel, buf, strlen(buf));
> >> +    if (ret) {
> >> +        goto out_free;
> >> +    }
> >> +
> >> +    g_io_channel_flush(channel,&err);
> >> +    if (err != NULL) {
> >> +        g_warning("error flushing payload: %s", err->message);
> >> +        ret = err->code;
> >> +        goto out_free;
> >> +    }
> >> +
> >> +out_free:
> >> +    QDECREF(payload_qstr);
> >> +    if (err) {
> >> +        g_error_free(err);
> >> +    }
> >> +    return ret;
> >> +}
> >> +
> >> +static void process_command(GAState *s, QDict *req)
> >> +{
> >> +    QObject *rsp = NULL;
> >> +    int ret;
> >> +
> >> +    g_assert(req);
> >> +    g_debug("processing command");
> >> +    rsp = qmp_dispatch(QOBJECT(req));
> >> +    if (rsp) {
> >> +        ret = conn_channel_send_payload(s->conn_channel, rsp);
> >> +        if (ret) {
> >> +            g_warning("error sending payload: %s", strerror(ret));
> >> +        }
> >> +        qobject_decref(rsp);
> >> +    } else {
> >> +        g_warning("error getting response");
> >> +    }
> >> +}
> >> +
> >> +/* handle requests/control events coming in over the channel */
> >> +static void process_event(JSONMessageParser *parser, QList *tokens)
> >> +{
> >> +    GAState *s = container_of(parser, GAState, parser);
> >> +    QObject *obj;
> >> +    QDict *qdict;
> >> +    Error *err = NULL;
> >> +    int ret;
> >> +
> >> +    g_assert(s&&  parser);
> >> +
> >> +    g_debug("process_event: called");
> >> +    obj = json_parser_parse_err(tokens, NULL,&err);
> >> +    if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
> >> +        qobject_decref(obj);
> >> +        qdict = qdict_new();
> >> +        if (!err) {
> >> +            g_warning("failed to parse event: unknown error");
> >> +            error_set(&err, QERR_JSON_PARSING);
> >> +        } else {
> >> +            g_warning("failed to parse event: %s", error_get_pretty(err));
> >> +        }
> >> +        qdict_put_obj(qdict, "error", error_get_qobject(err));
> >> +        error_free(err);
> >> +    } else {
> >> +        qdict = qobject_to_qdict(obj);
> >> +    }
> >> +
> >> +    g_assert(qdict);
> >> +
> >> +    /* handle host->guest commands */
> >> +    if (qdict_haskey(qdict, "execute")) {
> >> +        process_command(s, qdict);
> >> +    } else {
> >> +        if (!qdict_haskey(qdict, "error")) {
> >> +            QDECREF(qdict);
> >> +            qdict = qdict_new();
> >> +            g_warning("unrecognized payload format");
> >> +            error_set(&err, QERR_UNSUPPORTED);
> >> +            qdict_put_obj(qdict, "error", error_get_qobject(err));
> >> +            error_free(err);
> >> +        }
> >> +        ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
> >> +        if (ret) {
> >> +            g_warning("error sending payload: %s", strerror(ret));
> >> +        }
> >> +    }
> >> +
> >> +    QDECREF(qdict);
> >> +}
> >> +
> >> +static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
> >> +                                  gpointer data)
> >> +{
> >> +    GAState *s = data;
> >> +    gchar buf[1024];
> >> +    gsize count;
> >> +    GError *err = NULL;
> >> +    memset(buf, 0, 1024);
> >> +    GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
> >> +&count,&err);
> >> +    if (err != NULL) {
> >> +        g_warning("error reading channel: %s", err->message);
> >> +        conn_channel_close(s);
> >> +        g_error_free(err);
> >> +        return false;
> >> +    }
> >> +    switch (status) {
> >> +    case G_IO_STATUS_ERROR:
> >> +        g_warning("problem");
> >> +        return false;
> >> +    case G_IO_STATUS_NORMAL:
> >> +        g_debug("read data, count: %d, data: %s", (int)count, buf);
> >> +        json_message_parser_feed(&s->parser, (char *)buf, (int)count);
> >> +    case G_IO_STATUS_AGAIN:
> >> +        /* virtio causes us to spin here when no process is attached to
> >> +         * host-side chardev. sleep a bit to mitigate this
> >> +         */
> >> +        if (s->virtio) {
> >> +            usleep(100*1000);
> >> +        }
> >> +        return true;
> >> +    case G_IO_STATUS_EOF:
> >> +        g_debug("received EOF");
> >> +        conn_channel_close(s);
> >> +        if (s->virtio) {
> >> +            return true;
> >> +        }
> >> +        return false;
> >> +    default:
> >> +        g_warning("unknown channel read status, closing");
> >> +        conn_channel_close(s);
> >> +        return false;
> >> +    }
> >> +    return true;
> >> +}
> >> +
> >> +static int conn_channel_add(GAState *s, int fd)
> >> +{
> >> +    GIOChannel *conn_channel;
> >> +    guint conn_id;
> >> +    GError *err = NULL;
> >> +
> >> +    g_assert(s&&  !s->conn_channel);
> >> +    conn_channel = g_io_channel_unix_new(fd);
> >> +    g_assert(conn_channel);
> >> +    g_io_channel_set_encoding(conn_channel, NULL,&err);
> >> +    if (err != NULL) {
> >> +        g_warning("error setting channel encoding to binary");
> >> +        g_error_free(err);
> >> +        return -1;
> >> +    }
> >> +    conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
> >> +                             conn_channel_read, s);
> >> +    if (err != NULL) {
> >> +        g_warning("error adding io watch: %s", err->message);
> >> +        g_error_free(err);
> >> +        return -1;
> >> +    }
> >> +    s->conn_channel = conn_channel;
> >> +    s->conn_id = conn_id;
> >> +    return 0;
> >> +}
> >> +
> >> +static gboolean listen_channel_accept(GIOChannel *channel,
> >> +                                      GIOCondition condition, gpointer data)
> >> +{
> >> +    GAState *s = data;
> >> +    GError *err = NULL;
> >> +    g_assert(channel != NULL);
> >> +    int ret;
> >> +    bool accepted = false;
> >> +
> >> +    s->conn_sock = g_socket_accept(s->listen_sock, NULL,&err);
> >> +    if (err != NULL) {
> >> +        g_warning("error converting fd to gsocket: %s", err->message);
> >> +        g_error_free(err);
> >> +        goto out;
> >> +    }
> >> +    ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
> >> +    if (ret) {
> >> +        g_warning("error setting up connection");
> >> +        goto out;
> >> +    }
> >> +    accepted = true;
> >> +
> >> +out:
> >> +    /* only accept 1 connection at a time */
> >> +    return !accepted;
> >> +}
> >> +
> >> +/* start polling for readable events on listen fd, new==true
> >> + * indicates we should use the existing s->listen_channel
> >> + */
> >> +static int listen_channel_add(GAState *s, int listen_fd, bool new)
> >> +{
> >> +    GError *err = NULL;
> >> +    guint listen_id;
> >> +
> >> +    if (new) {
> >> +        s->listen_channel = g_io_channel_unix_new(listen_fd);
> >> +        if (s->listen_sock) {
> >> +            g_object_unref(s->listen_sock);
> >> +        }
> >> +        s->listen_sock = g_socket_new_from_fd(listen_fd,&err);
> >> +        if (err != NULL) {
> >> +            g_warning("error converting fd to gsocket: %s", err->message);
> >> +            g_error_free(err);
> >> +            return -1;
> >> +        }
> >> +    }
> >> +    listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
> >> +                               listen_channel_accept, s);
> >> +    if (err != NULL) {
> >> +        g_warning("error adding io watch: %s", err->message);
> >> +        g_error_free(err);
> >> +        return -1;
> >> +    }
> >> +    return 0;
> >
> > I'm getting the following error when building this on a F15 guest:
> >
> > qemu-ga.c: In function ‘listen_channel_add’:
> > qemu-ga.c:453:11: error: variable ‘listen_id’ set but not used [-Werror=unused-but-set-variable]
> > cc1: all warnings being treated as errors
> >
> > g_io_add_watch() doesn't seem to return an error. So if the fix is just to
> > drop 'listen_id' and the last if statement altogether I can do it myself,
> > otherwise you can send me only this patch.
> >
> > The series looks good otherwise.
> >
> 
> Doh, sorry for not catching this. I did the same thing with conn_id as 
> well. I made a patch available here:
> 
> http://repo.or.cz/w/qemu/mdroth.git/blobdiff/a8d38e5bb2d558918ac0bad1ee618472ae34fe7a..581922e148f37e8f5844eeb11257eef8e9302709:/qemu-ga.c
> 
> I tested the patch on a 32-bit F15 guest and it seems to do the trick.
> 
> If you'd like you can just pull from
> 
> git://repo.or.cz/qemu/mdroth.git qga-dev
> 
> Which is this series (set3-v8) plus the patch right now.

Can you resend the patch to the list please? If it's just this patch
(ie. patch 3/4) that was rebased, then you can submit only it. Otherwise
would be better to submit a new version of the series.

> 
> >> +}
> >> +
> >> +/* cleanup state for closed connection/session, start accepting new
> >> + * connections if we're in listening mode
> >> + */
> >> +static void conn_channel_close(GAState *s)
> >> +{
> >> +    if (strcmp(s->method, "unix-listen") == 0) {
> >> +        g_io_channel_shutdown(s->conn_channel, true, NULL);
> >> +        g_object_unref(s->conn_sock);
> >> +        s->conn_sock = NULL;
> >> +        listen_channel_add(s, 0, false);
> >> +    } else if (strcmp(s->method, "virtio-serial") == 0) {
> >> +        /* we spin on EOF for virtio-serial, so back off a bit. also,
> >> +         * dont close the connection in this case, it'll resume normal
> >> +         * operation when another process connects to host chardev
> >> +         */
> >> +        usleep(100*1000);
> >> +        goto out_noclose;
> >> +    }
> >> +    g_io_channel_unref(s->conn_channel);
> >> +    s->conn_channel = NULL;
> >> +    s->conn_id = 0;
> >> +out_noclose:
> >> +    return;
> >> +}
> >> +
> >> +static void init_guest_agent(GAState *s)
> >> +{
> >> +    struct termios tio;
> >> +    int ret, fd;
> >> +
> >> +    if (s->method == NULL) {
> >> +        /* try virtio-serial as our default */
> >> +        s->method = "virtio-serial";
> >> +    }
> >> +
> >> +    if (s->path == NULL) {
> >> +        if (strcmp(s->method, "virtio-serial") != 0) {
> >> +            g_critical("must specify a path for this channel");
> >> +            exit(EXIT_FAILURE);
> >> +        }
> >> +        /* try the default path for the virtio-serial port */
> >> +        s->path = QGA_VIRTIO_PATH_DEFAULT;
> >> +    }
> >> +
> >> +    if (strcmp(s->method, "virtio-serial") == 0) {
> >> +        s->virtio = true;
> >> +        fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
> >> +        if (fd == -1) {
> >> +            g_critical("error opening channel: %s", strerror(errno));
> >> +            exit(EXIT_FAILURE);
> >> +        }
> >> +        ret = conn_channel_add(s, fd);
> >> +        if (ret) {
> >> +            g_critical("error adding channel to main loop");
> >> +            exit(EXIT_FAILURE);
> >> +        }
> >> +    } else if (strcmp(s->method, "isa-serial") == 0) {
> >> +        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
> >> +        if (fd == -1) {
> >> +            g_critical("error opening channel: %s", strerror(errno));
> >> +            exit(EXIT_FAILURE);
> >> +        }
> >> +        tcgetattr(fd,&tio);
> >> +        /* set up serial port for non-canonical, dumb byte streaming */
> >> +        tio.c_iflag&= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
> >> +                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
> >> +                         IMAXBEL);
> >> +        tio.c_oflag = 0;
> >> +        tio.c_lflag = 0;
> >> +        tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
> >> +        /* 1 available byte min or reads will block (we'll set non-blocking
> >> +         * elsewhere, else we have to deal with read()=0 instead)
> >> +         */
> >> +        tio.c_cc[VMIN] = 1;
> >> +        tio.c_cc[VTIME] = 0;
> >> +        /* flush everything waiting for read/xmit, it's garbage at this point */
> >> +        tcflush(fd, TCIFLUSH);
> >> +        tcsetattr(fd, TCSANOW,&tio);
> >> +        ret = conn_channel_add(s, fd);
> >> +        if (ret) {
> >> +            g_error("error adding channel to main loop");
> >> +        }
> >> +    } else if (strcmp(s->method, "unix-listen") == 0) {
> >> +        fd = unix_listen(s->path, NULL, strlen(s->path));
> >> +        if (fd == -1) {
> >> +            g_critical("error opening path: %s", strerror(errno));
> >> +            exit(EXIT_FAILURE);
> >> +        }
> >> +        ret = listen_channel_add(s, fd, true);
> >> +        if (ret) {
> >> +            g_critical("error binding/listening to specified socket");
> >> +            exit(EXIT_FAILURE);
> >> +        }
> >> +    } else {
> >> +        g_critical("unsupported channel method/type: %s", s->method);
> >> +        exit(EXIT_FAILURE);
> >> +    }
> >> +
> >> +    json_message_parser_init(&s->parser, process_event);
> >> +    s->main_loop = g_main_loop_new(NULL, false);
> >> +}
> >> +
> >> +int main(int argc, char **argv)
> >> +{
> >> +    const char *sopt = "hVvdm:p:l:f:";
> >> +    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
> >> +    const struct option lopt[] = {
> >> +        { "help", 0, NULL, 'h' },
> >> +        { "version", 0, NULL, 'V' },
> >> +        { "logfile", 0, NULL, 'l' },
> >> +        { "pidfile", 0, NULL, 'f' },
> >> +        { "verbose", 0, NULL, 'v' },
> >> +        { "method", 0, NULL, 'm' },
> >> +        { "path", 0, NULL, 'p' },
> >> +        { "daemonize", 0, NULL, 'd' },
> >> +        { NULL, 0, NULL, 0 }
> >> +    };
> >> +    int opt_ind = 0, ch, daemonize = 0;
> >> +    GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
> >> +    FILE *log_file = stderr;
> >> +    GAState *s;
> >> +
> >> +    while ((ch = getopt_long(argc, argv, sopt, lopt,&opt_ind)) != -1) {
> >> +        switch (ch) {
> >> +        case 'm':
> >> +            method = optarg;
> >> +            break;
> >> +        case 'p':
> >> +            path = optarg;
> >> +            break;
> >> +        case 'l':
> >> +            log_file = fopen(optarg, "a");
> >> +            if (!log_file) {
> >> +                g_critical("unable to open specified log file: %s",
> >> +                           strerror(errno));
> >> +                return EXIT_FAILURE;
> >> +            }
> >> +            break;
> >> +        case 'f':
> >> +            pidfile = optarg;
> >> +            break;
> >> +        case 'v':
> >> +            /* enable all log levels */
> >> +            log_level = G_LOG_LEVEL_MASK;
> >> +            break;
> >> +        case 'V':
> >> +            printf("QEMU Guest Agent %s\n", QGA_VERSION);
> >> +            return 0;
> >> +        case 'd':
> >> +            daemonize = 1;
> >> +            break;
> >> +        case 'h':
> >> +            usage(argv[0]);
> >> +            return 0;
> >> +        case '?':
> >> +            g_print("Unknown option, try '%s --help' for more information.\n",
> >> +                    argv[0]);
> >> +            return EXIT_FAILURE;
> >> +        }
> >> +    }
> >> +
> >> +    if (daemonize) {
> >> +        g_debug("starting daemon");
> >> +        become_daemon(pidfile);
> >> +    }
> >> +
> >> +    g_type_init();
> >> +    g_thread_init(NULL);
> >> +
> >> +    s = qemu_mallocz(sizeof(GAState));
> >> +    s->conn_id = 0;
> >> +    s->conn_channel = NULL;
> >> +    s->path = path;
> >> +    s->method = method;
> >> +    s->log_file = log_file;
> >> +    s->log_level = log_level;
> >> +    g_log_set_default_handler(ga_log, s);
> >> +    g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
> >> +    s->logging_enabled = true;
> >> +    ga_state = s;
> >> +
> >> +    module_call_init(MODULE_INIT_QAPI);
> >> +    init_guest_agent(ga_state);
> >> +    register_signal_handlers();
> >> +
> >> +    g_main_loop_run(ga_state->main_loop);
> >> +
> >> +    unlink(pidfile);
> >> +
> >> +    return 0;
> >> +}
> >> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
> >> index 688f120..66d1729 100644
> >> --- a/qga/guest-agent-core.h
> >> +++ b/qga/guest-agent-core.h
> >> @@ -15,6 +15,7 @@
> >>
> >>   #define QGA_VERSION "1.0"
> >>
> >> +typedef struct GAState GAState;
> >>   typedef struct GACommandState GACommandState;
> >>
> >>   void ga_command_state_add(GACommandState *cs,
> >> @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
> >>   void ga_command_state_init_all(GACommandState *cs);
> >>   void ga_command_state_cleanup_all(GACommandState *cs);
> >>   GACommandState *ga_command_state_new(void);
> >> +bool ga_logging_enabled(GAState *s);
> >> +void ga_disable_logging(GAState *s);
> >> +void ga_enable_logging(GAState *s);
> >
> 

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

* [Qemu-devel] [PATCH] guest agent: qemu-ga, remove unused variables
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon Michael Roth
  2011-07-20 17:56   ` Luiz Capitulino
@ 2011-07-20 20:00   ` Michael Roth
  2011-07-20 20:19   ` [Qemu-devel] [PATCH] guest agent: qemu-ga daemon Michael Roth
  2 siblings, 0 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-20 20:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino

---
 qemu-ga.c |   25 ++++---------------------
 1 files changed, 4 insertions(+), 21 deletions(-)

diff --git a/qemu-ga.c b/qemu-ga.c
index 4530d3d..6e2f61f 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -37,10 +37,8 @@
 struct GAState {
     JSONMessageParser parser;
     GMainLoop *main_loop;
-    guint conn_id;
     GSocket *conn_sock;
     GIOChannel *conn_channel;
-    guint listen_id;
     GSocket *listen_sock;
     GIOChannel *listen_channel;
     const char *path;
@@ -393,7 +391,6 @@ static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
 static int conn_channel_add(GAState *s, int fd)
 {
     GIOChannel *conn_channel;
-    guint conn_id;
     GError *err = NULL;
 
     g_assert(s && !s->conn_channel);
@@ -405,15 +402,9 @@ static int conn_channel_add(GAState *s, int fd)
         g_error_free(err);
         return -1;
     }
-    conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
-                             conn_channel_read, s);
-    if (err != NULL) {
-        g_warning("error adding io watch: %s", err->message);
-        g_error_free(err);
-        return -1;
-    }
+    g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
+                   conn_channel_read, s);
     s->conn_channel = conn_channel;
-    s->conn_id = conn_id;
     return 0;
 }
 
@@ -450,7 +441,6 @@ out:
 static int listen_channel_add(GAState *s, int listen_fd, bool new)
 {
     GError *err = NULL;
-    guint listen_id;
 
     if (new) {
         s->listen_channel = g_io_channel_unix_new(listen_fd);
@@ -464,13 +454,8 @@ static int listen_channel_add(GAState *s, int listen_fd, bool new)
             return -1;
         }
     }
-    listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
-                               listen_channel_accept, s);
-    if (err != NULL) {
-        g_warning("error adding io watch: %s", err->message);
-        g_error_free(err);
-        return -1;
-    }
+    g_io_add_watch(s->listen_channel, G_IO_IN,
+                   listen_channel_accept, s);
     return 0;
 }
 
@@ -494,7 +479,6 @@ static void conn_channel_close(GAState *s)
     }
     g_io_channel_unref(s->conn_channel);
     s->conn_channel = NULL;
-    s->conn_id = 0;
 out_noclose:
     return;
 }
@@ -644,7 +628,6 @@ int main(int argc, char **argv)
     g_thread_init(NULL);
 
     s = qemu_mallocz(sizeof(GAState));
-    s->conn_id = 0;
     s->conn_channel = NULL;
     s->path = path;
     s->method = method;
-- 
1.7.0.4

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

* Re: [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-20 19:31       ` Luiz Capitulino
@ 2011-07-20 20:01         ` Michael Roth
  2011-07-20 20:05           ` Michael Roth
  0 siblings, 1 reply; 30+ messages in thread
From: Michael Roth @ 2011-07-20 20:01 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: aliguori, agl, qemu-devel, Jes.Sorensen

On 07/20/2011 02:31 PM, Luiz Capitulino wrote:
> On Wed, 20 Jul 2011 14:25:06 -0500
> Michael Roth<mdroth@linux.vnet.ibm.com>  wrote:
>
>> On 07/20/2011 12:56 PM, Luiz Capitulino wrote:
>>> On Tue, 19 Jul 2011 15:41:54 -0500
>>> Michael Roth<mdroth@linux.vnet.ibm.com>   wrote:
>>>
>>>> This is the actual guest daemon, it listens for requests over a
>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>> to dispatch routines, and writes the results back to the channel in
>>>> a manner similar to QMP.
>>>>
>>>> A shorthand invocation:
>>>>
>>>>     qemu-ga -d
>>>>
>>>> Is equivalent to:
>>>>
>>>>     qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>             -f /var/run/qemu-ga.pid -d
>>>>
>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>> ---
>>>>    Makefile               |    8 +-
>>>>    configure              |    1 +
>>>>    qemu-ga.c              |  667 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    qga/guest-agent-core.h |    4 +
>>>>    4 files changed, 677 insertions(+), 3 deletions(-)
>>>>    create mode 100644 qemu-ga.c
>>>>
>>>> diff --git a/Makefile b/Makefile
>>>> index b8cdf0e..0d2e33d 100644
>>>> --- a/Makefile
>>>> +++ b/Makefile
>>>> @@ -140,7 +140,7 @@ endif
>>>>    ######################################################################
>>>>
>>>>    qemu-img.o: qemu-img-cmds.h
>>>> -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
>>>> +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
>>>>
>>>>    qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
>>>>
>>>> @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o
>>>>
>>>>    QGALIB=qga/guest-agent-command-state.o
>>>>
>>>> +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
>>>> +
>>>>    QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
>>>>
>>>>    clean:
>>>>    # avoid old build problems by removing potentially incorrect old files
>>>>    	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
>>>>    	rm -f qemu-options.def
>>>> -	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
>>>> +	rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
>>>>    	rm -Rf .libs
>>>>    	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
>>>>    	rm -f qemu-img-cmds.h
>>>> @@ -386,4 +388,4 @@ tarbin:
>>>>    	$(mandir)/man8/qemu-nbd.8
>>>>
>>>>    # Include automatically generated dependency files
>>>> --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d)
>>>> +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
>>>> diff --git a/configure b/configure
>>>> index 6a03002..445f674 100755
>>>> --- a/configure
>>>> +++ b/configure
>>>> @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
>>>>      tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
>>>>      if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
>>>>          tools="qemu-nbd\$(EXESUF) $tools"
>>>> +      tools="qemu-ga\$(EXESUF) $tools"
>>>>        if [ "$check_utests" = "yes" ]; then
>>>>          tools="check-qint check-qstring check-qdict check-qlist $tools"
>>>>          tools="check-qfloat check-qjson $tools"
>>>> diff --git a/qemu-ga.c b/qemu-ga.c
>>>> new file mode 100644
>>>> index 0000000..eb09100
>>>> --- /dev/null
>>>> +++ b/qemu-ga.c
>>>> @@ -0,0 +1,667 @@
>>>> +/*
>>>> + * QEMU Guest Agent
>>>> + *
>>>> + * Copyright IBM Corp. 2011
>>>> + *
>>>> + * Authors:
>>>> + *  Adam Litke<aglitke@linux.vnet.ibm.com>
>>>> + *  Michael Roth<mdroth@linux.vnet.ibm.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<stdlib.h>
>>>> +#include<stdio.h>
>>>> +#include<stdbool.h>
>>>> +#include<glib.h>
>>>> +#include<gio/gio.h>
>>>> +#include<getopt.h>
>>>> +#include<termios.h>
>>>> +#include<syslog.h>
>>>> +#include "qemu_socket.h"
>>>> +#include "json-streamer.h"
>>>> +#include "json-parser.h"
>>>> +#include "qint.h"
>>>> +#include "qjson.h"
>>>> +#include "qga/guest-agent-core.h"
>>>> +#include "module.h"
>>>> +#include "signal.h"
>>>> +#include "qerror.h"
>>>> +#include "error_int.h"
>>>> +
>>>> +#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
>>>> +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
>>>> +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
>>>> +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
>>>> +
>>>> +struct GAState {
>>>> +    JSONMessageParser parser;
>>>> +    GMainLoop *main_loop;
>>>> +    guint conn_id;
>>>> +    GSocket *conn_sock;
>>>> +    GIOChannel *conn_channel;
>>>> +    guint listen_id;
>>>> +    GSocket *listen_sock;
>>>> +    GIOChannel *listen_channel;
>>>> +    const char *path;
>>>> +    const char *method;
>>>> +    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
>>>> +    GACommandState *command_state;
>>>> +    GLogLevelFlags log_level;
>>>> +    FILE *log_file;
>>>> +    bool logging_enabled;
>>>> +};
>>>> +
>>>> +static struct GAState *ga_state;
>>>> +
>>>> +static void quit_handler(int sig)
>>>> +{
>>>> +    g_debug("recieved signal num %d, quitting", sig);
>>>> +
>>>> +    if (g_main_loop_is_running(ga_state->main_loop)) {
>>>> +        g_main_loop_quit(ga_state->main_loop);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void register_signal_handlers(void)
>>>> +{
>>>> +    struct sigaction sigact;
>>>> +    int ret;
>>>> +
>>>> +    memset(&sigact, 0, sizeof(struct sigaction));
>>>> +    sigact.sa_handler = quit_handler;
>>>> +
>>>> +    ret = sigaction(SIGINT,&sigact, NULL);
>>>> +    if (ret == -1) {
>>>> +        g_error("error configuring signal handler: %s", strerror(errno));
>>>> +        exit(EXIT_FAILURE);
>>>> +    }
>>>> +    ret = sigaction(SIGTERM,&sigact, NULL);
>>>> +    if (ret == -1) {
>>>> +        g_error("error configuring signal handler: %s", strerror(errno));
>>>> +    }
>>>> +}
>>>> +
>>>> +static void usage(const char *cmd)
>>>> +{
>>>> +    printf(
>>>> +"Usage: %s -c<channel_opts>\n"
>>>> +"QEMU Guest Agent %s\n"
>>>> +"\n"
>>>> +"  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
>>>> +"                    isa-serial (virtio-serial is the default)\n"
>>>> +"  -p, --path        device/socket path (%s is the default for virtio-serial)\n"
>>>> +"  -l, --logfile     set logfile path, logs to stderr by default\n"
>>>> +"  -f, --pidfile     specify pidfile (default is %s)\n"
>>>> +"  -v, --verbose     log extra debugging information\n"
>>>> +"  -V, --version     print version information and exit\n"
>>>> +"  -d, --daemonize   become a daemon\n"
>>>> +"  -h, --help        display this help and exit\n"
>>>> +"\n"
>>>> +"Report bugs to<mdroth@linux.vnet.ibm.com>\n"
>>>> +    , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
>>>> +}
>>>> +
>>>> +static void conn_channel_close(GAState *s);
>>>> +
>>>> +static const char *ga_log_level_str(GLogLevelFlags level)
>>>> +{
>>>> +    switch (level&   G_LOG_LEVEL_MASK) {
>>>> +        case G_LOG_LEVEL_ERROR:
>>>> +            return "error";
>>>> +        case G_LOG_LEVEL_CRITICAL:
>>>> +            return "critical";
>>>> +        case G_LOG_LEVEL_WARNING:
>>>> +            return "warning";
>>>> +        case G_LOG_LEVEL_MESSAGE:
>>>> +            return "message";
>>>> +        case G_LOG_LEVEL_INFO:
>>>> +            return "info";
>>>> +        case G_LOG_LEVEL_DEBUG:
>>>> +            return "debug";
>>>> +        default:
>>>> +            return "user";
>>>> +    }
>>>> +}
>>>> +
>>>> +bool ga_logging_enabled(GAState *s)
>>>> +{
>>>> +    return s->logging_enabled;
>>>> +}
>>>> +
>>>> +void ga_disable_logging(GAState *s)
>>>> +{
>>>> +    s->logging_enabled = false;
>>>> +}
>>>> +
>>>> +void ga_enable_logging(GAState *s)
>>>> +{
>>>> +    s->logging_enabled = true;
>>>> +}
>>>> +
>>>> +static void ga_log(const gchar *domain, GLogLevelFlags level,
>>>> +                   const gchar *msg, gpointer opaque)
>>>> +{
>>>> +    GAState *s = opaque;
>>>> +    GTimeVal time;
>>>> +    const char *level_str = ga_log_level_str(level);
>>>> +
>>>> +    if (!ga_logging_enabled(s)) {
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    level&= G_LOG_LEVEL_MASK;
>>>> +    if (g_strcmp0(domain, "syslog") == 0) {
>>>> +        syslog(LOG_INFO, "%s: %s", level_str, msg);
>>>> +    } else if (level&   s->log_level) {
>>>> +        g_get_current_time(&time);
>>>> +        fprintf(s->log_file,
>>>> +                "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
>>>> +        fflush(s->log_file);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void become_daemon(const char *pidfile)
>>>> +{
>>>> +    pid_t pid, sid;
>>>> +    int pidfd;
>>>> +    char *pidstr = NULL;
>>>> +
>>>> +    pid = fork();
>>>> +    if (pid<   0) {
>>>> +        exit(EXIT_FAILURE);
>>>> +    }
>>>> +    if (pid>   0) {
>>>> +        exit(EXIT_SUCCESS);
>>>> +    }
>>>> +
>>>> +    pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
>>>> +    if (pidfd == -1) {
>>>> +        g_critical("Cannot create pid file, %s", strerror(errno));
>>>> +        exit(EXIT_FAILURE);
>>>> +    }
>>>> +
>>>> +    if (asprintf(&pidstr, "%d", getpid()) == -1) {
>>>> +        g_critical("Cannot allocate memory");
>>>> +        goto fail;
>>>> +    }
>>>> +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
>>>> +        free(pidstr);
>>>> +        g_critical("Failed to write pid file");
>>>> +        goto fail;
>>>> +    }
>>>> +
>>>> +    umask(0);
>>>> +    sid = setsid();
>>>> +    if (sid<   0) {
>>>> +        goto fail;
>>>> +    }
>>>> +    if ((chdir("/"))<   0) {
>>>> +        goto fail;
>>>> +    }
>>>> +
>>>> +    close(STDIN_FILENO);
>>>> +    close(STDOUT_FILENO);
>>>> +    close(STDERR_FILENO);
>>>> +    free(pidstr);
>>>> +    return;
>>>> +
>>>> +fail:
>>>> +    unlink(pidfile);
>>>> +    g_critical("failed to daemonize");
>>>> +    exit(EXIT_FAILURE);
>>>> +}
>>>> +
>>>> +static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
>>>> +                                 gsize count)
>>>> +{
>>>> +    GError *err = NULL;
>>>> +    gsize written = 0;
>>>> +    GIOStatus status;
>>>> +
>>>> +    while (count) {
>>>> +        status = g_io_channel_write_chars(channel, buf, count,&written,&err);
>>>> +        g_debug("sending data, count: %d", (int)count);
>>>> +        if (err != NULL) {
>>>> +            g_warning("error sending newline: %s", err->message);
>>>> +            return err->code;
>>>> +        }
>>>> +        if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
>>>> +            return -EPIPE;
>>>> +        }
>>>> +
>>>> +        if (status == G_IO_STATUS_NORMAL) {
>>>> +            count -= written;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
>>>> +{
>>>> +    int ret = 0;
>>>> +    const char *buf;
>>>> +    QString *payload_qstr;
>>>> +    GError *err = NULL;
>>>> +
>>>> +    g_assert(payload&&   channel);
>>>> +
>>>> +    payload_qstr = qobject_to_json(payload);
>>>> +    if (!payload_qstr) {
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    qstring_append_chr(payload_qstr, '\n');
>>>> +    buf = qstring_get_str(payload_qstr);
>>>> +    ret = conn_channel_send_buf(channel, buf, strlen(buf));
>>>> +    if (ret) {
>>>> +        goto out_free;
>>>> +    }
>>>> +
>>>> +    g_io_channel_flush(channel,&err);
>>>> +    if (err != NULL) {
>>>> +        g_warning("error flushing payload: %s", err->message);
>>>> +        ret = err->code;
>>>> +        goto out_free;
>>>> +    }
>>>> +
>>>> +out_free:
>>>> +    QDECREF(payload_qstr);
>>>> +    if (err) {
>>>> +        g_error_free(err);
>>>> +    }
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static void process_command(GAState *s, QDict *req)
>>>> +{
>>>> +    QObject *rsp = NULL;
>>>> +    int ret;
>>>> +
>>>> +    g_assert(req);
>>>> +    g_debug("processing command");
>>>> +    rsp = qmp_dispatch(QOBJECT(req));
>>>> +    if (rsp) {
>>>> +        ret = conn_channel_send_payload(s->conn_channel, rsp);
>>>> +        if (ret) {
>>>> +            g_warning("error sending payload: %s", strerror(ret));
>>>> +        }
>>>> +        qobject_decref(rsp);
>>>> +    } else {
>>>> +        g_warning("error getting response");
>>>> +    }
>>>> +}
>>>> +
>>>> +/* handle requests/control events coming in over the channel */
>>>> +static void process_event(JSONMessageParser *parser, QList *tokens)
>>>> +{
>>>> +    GAState *s = container_of(parser, GAState, parser);
>>>> +    QObject *obj;
>>>> +    QDict *qdict;
>>>> +    Error *err = NULL;
>>>> +    int ret;
>>>> +
>>>> +    g_assert(s&&   parser);
>>>> +
>>>> +    g_debug("process_event: called");
>>>> +    obj = json_parser_parse_err(tokens, NULL,&err);
>>>> +    if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
>>>> +        qobject_decref(obj);
>>>> +        qdict = qdict_new();
>>>> +        if (!err) {
>>>> +            g_warning("failed to parse event: unknown error");
>>>> +            error_set(&err, QERR_JSON_PARSING);
>>>> +        } else {
>>>> +            g_warning("failed to parse event: %s", error_get_pretty(err));
>>>> +        }
>>>> +        qdict_put_obj(qdict, "error", error_get_qobject(err));
>>>> +        error_free(err);
>>>> +    } else {
>>>> +        qdict = qobject_to_qdict(obj);
>>>> +    }
>>>> +
>>>> +    g_assert(qdict);
>>>> +
>>>> +    /* handle host->guest commands */
>>>> +    if (qdict_haskey(qdict, "execute")) {
>>>> +        process_command(s, qdict);
>>>> +    } else {
>>>> +        if (!qdict_haskey(qdict, "error")) {
>>>> +            QDECREF(qdict);
>>>> +            qdict = qdict_new();
>>>> +            g_warning("unrecognized payload format");
>>>> +            error_set(&err, QERR_UNSUPPORTED);
>>>> +            qdict_put_obj(qdict, "error", error_get_qobject(err));
>>>> +            error_free(err);
>>>> +        }
>>>> +        ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
>>>> +        if (ret) {
>>>> +            g_warning("error sending payload: %s", strerror(ret));
>>>> +        }
>>>> +    }
>>>> +
>>>> +    QDECREF(qdict);
>>>> +}
>>>> +
>>>> +static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
>>>> +                                  gpointer data)
>>>> +{
>>>> +    GAState *s = data;
>>>> +    gchar buf[1024];
>>>> +    gsize count;
>>>> +    GError *err = NULL;
>>>> +    memset(buf, 0, 1024);
>>>> +    GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
>>>> +&count,&err);
>>>> +    if (err != NULL) {
>>>> +        g_warning("error reading channel: %s", err->message);
>>>> +        conn_channel_close(s);
>>>> +        g_error_free(err);
>>>> +        return false;
>>>> +    }
>>>> +    switch (status) {
>>>> +    case G_IO_STATUS_ERROR:
>>>> +        g_warning("problem");
>>>> +        return false;
>>>> +    case G_IO_STATUS_NORMAL:
>>>> +        g_debug("read data, count: %d, data: %s", (int)count, buf);
>>>> +        json_message_parser_feed(&s->parser, (char *)buf, (int)count);
>>>> +    case G_IO_STATUS_AGAIN:
>>>> +        /* virtio causes us to spin here when no process is attached to
>>>> +         * host-side chardev. sleep a bit to mitigate this
>>>> +         */
>>>> +        if (s->virtio) {
>>>> +            usleep(100*1000);
>>>> +        }
>>>> +        return true;
>>>> +    case G_IO_STATUS_EOF:
>>>> +        g_debug("received EOF");
>>>> +        conn_channel_close(s);
>>>> +        if (s->virtio) {
>>>> +            return true;
>>>> +        }
>>>> +        return false;
>>>> +    default:
>>>> +        g_warning("unknown channel read status, closing");
>>>> +        conn_channel_close(s);
>>>> +        return false;
>>>> +    }
>>>> +    return true;
>>>> +}
>>>> +
>>>> +static int conn_channel_add(GAState *s, int fd)
>>>> +{
>>>> +    GIOChannel *conn_channel;
>>>> +    guint conn_id;
>>>> +    GError *err = NULL;
>>>> +
>>>> +    g_assert(s&&   !s->conn_channel);
>>>> +    conn_channel = g_io_channel_unix_new(fd);
>>>> +    g_assert(conn_channel);
>>>> +    g_io_channel_set_encoding(conn_channel, NULL,&err);
>>>> +    if (err != NULL) {
>>>> +        g_warning("error setting channel encoding to binary");
>>>> +        g_error_free(err);
>>>> +        return -1;
>>>> +    }
>>>> +    conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
>>>> +                             conn_channel_read, s);
>>>> +    if (err != NULL) {
>>>> +        g_warning("error adding io watch: %s", err->message);
>>>> +        g_error_free(err);
>>>> +        return -1;
>>>> +    }
>>>> +    s->conn_channel = conn_channel;
>>>> +    s->conn_id = conn_id;
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static gboolean listen_channel_accept(GIOChannel *channel,
>>>> +                                      GIOCondition condition, gpointer data)
>>>> +{
>>>> +    GAState *s = data;
>>>> +    GError *err = NULL;
>>>> +    g_assert(channel != NULL);
>>>> +    int ret;
>>>> +    bool accepted = false;
>>>> +
>>>> +    s->conn_sock = g_socket_accept(s->listen_sock, NULL,&err);
>>>> +    if (err != NULL) {
>>>> +        g_warning("error converting fd to gsocket: %s", err->message);
>>>> +        g_error_free(err);
>>>> +        goto out;
>>>> +    }
>>>> +    ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
>>>> +    if (ret) {
>>>> +        g_warning("error setting up connection");
>>>> +        goto out;
>>>> +    }
>>>> +    accepted = true;
>>>> +
>>>> +out:
>>>> +    /* only accept 1 connection at a time */
>>>> +    return !accepted;
>>>> +}
>>>> +
>>>> +/* start polling for readable events on listen fd, new==true
>>>> + * indicates we should use the existing s->listen_channel
>>>> + */
>>>> +static int listen_channel_add(GAState *s, int listen_fd, bool new)
>>>> +{
>>>> +    GError *err = NULL;
>>>> +    guint listen_id;
>>>> +
>>>> +    if (new) {
>>>> +        s->listen_channel = g_io_channel_unix_new(listen_fd);
>>>> +        if (s->listen_sock) {
>>>> +            g_object_unref(s->listen_sock);
>>>> +        }
>>>> +        s->listen_sock = g_socket_new_from_fd(listen_fd,&err);
>>>> +        if (err != NULL) {
>>>> +            g_warning("error converting fd to gsocket: %s", err->message);
>>>> +            g_error_free(err);
>>>> +            return -1;
>>>> +        }
>>>> +    }
>>>> +    listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
>>>> +                               listen_channel_accept, s);
>>>> +    if (err != NULL) {
>>>> +        g_warning("error adding io watch: %s", err->message);
>>>> +        g_error_free(err);
>>>> +        return -1;
>>>> +    }
>>>> +    return 0;
>>>
>>> I'm getting the following error when building this on a F15 guest:
>>>
>>> qemu-ga.c: In function ‘listen_channel_add’:
>>> qemu-ga.c:453:11: error: variable ‘listen_id’ set but not used [-Werror=unused-but-set-variable]
>>> cc1: all warnings being treated as errors
>>>
>>> g_io_add_watch() doesn't seem to return an error. So if the fix is just to
>>> drop 'listen_id' and the last if statement altogether I can do it myself,
>>> otherwise you can send me only this patch.
>>>
>>> The series looks good otherwise.
>>>
>>
>> Doh, sorry for not catching this. I did the same thing with conn_id as
>> well. I made a patch available here:
>>
>> http://repo.or.cz/w/qemu/mdroth.git/blobdiff/a8d38e5bb2d558918ac0bad1ee618472ae34fe7a..581922e148f37e8f5844eeb11257eef8e9302709:/qemu-ga.c
>>
>> I tested the patch on a 32-bit F15 guest and it seems to do the trick.
>>
>> If you'd like you can just pull from
>>
>> git://repo.or.cz/qemu/mdroth.git qga-dev
>>
>> Which is this series (set3-v8) plus the patch right now.
>
> Can you resend the patch to the list please? If it's just this patch
> (ie. patch 3/4) that was rebased, then you can submit only it. Otherwise
> would be better to submit a new version of the series.
>

Patch sent. It's just against 3/4

>>
>>>> +}
>>>> +
>>>> +/* cleanup state for closed connection/session, start accepting new
>>>> + * connections if we're in listening mode
>>>> + */
>>>> +static void conn_channel_close(GAState *s)
>>>> +{
>>>> +    if (strcmp(s->method, "unix-listen") == 0) {
>>>> +        g_io_channel_shutdown(s->conn_channel, true, NULL);
>>>> +        g_object_unref(s->conn_sock);
>>>> +        s->conn_sock = NULL;
>>>> +        listen_channel_add(s, 0, false);
>>>> +    } else if (strcmp(s->method, "virtio-serial") == 0) {
>>>> +        /* we spin on EOF for virtio-serial, so back off a bit. also,
>>>> +         * dont close the connection in this case, it'll resume normal
>>>> +         * operation when another process connects to host chardev
>>>> +         */
>>>> +        usleep(100*1000);
>>>> +        goto out_noclose;
>>>> +    }
>>>> +    g_io_channel_unref(s->conn_channel);
>>>> +    s->conn_channel = NULL;
>>>> +    s->conn_id = 0;
>>>> +out_noclose:
>>>> +    return;
>>>> +}
>>>> +
>>>> +static void init_guest_agent(GAState *s)
>>>> +{
>>>> +    struct termios tio;
>>>> +    int ret, fd;
>>>> +
>>>> +    if (s->method == NULL) {
>>>> +        /* try virtio-serial as our default */
>>>> +        s->method = "virtio-serial";
>>>> +    }
>>>> +
>>>> +    if (s->path == NULL) {
>>>> +        if (strcmp(s->method, "virtio-serial") != 0) {
>>>> +            g_critical("must specify a path for this channel");
>>>> +            exit(EXIT_FAILURE);
>>>> +        }
>>>> +        /* try the default path for the virtio-serial port */
>>>> +        s->path = QGA_VIRTIO_PATH_DEFAULT;
>>>> +    }
>>>> +
>>>> +    if (strcmp(s->method, "virtio-serial") == 0) {
>>>> +        s->virtio = true;
>>>> +        fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
>>>> +        if (fd == -1) {
>>>> +            g_critical("error opening channel: %s", strerror(errno));
>>>> +            exit(EXIT_FAILURE);
>>>> +        }
>>>> +        ret = conn_channel_add(s, fd);
>>>> +        if (ret) {
>>>> +            g_critical("error adding channel to main loop");
>>>> +            exit(EXIT_FAILURE);
>>>> +        }
>>>> +    } else if (strcmp(s->method, "isa-serial") == 0) {
>>>> +        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
>>>> +        if (fd == -1) {
>>>> +            g_critical("error opening channel: %s", strerror(errno));
>>>> +            exit(EXIT_FAILURE);
>>>> +        }
>>>> +        tcgetattr(fd,&tio);
>>>> +        /* set up serial port for non-canonical, dumb byte streaming */
>>>> +        tio.c_iflag&= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
>>>> +                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
>>>> +                         IMAXBEL);
>>>> +        tio.c_oflag = 0;
>>>> +        tio.c_lflag = 0;
>>>> +        tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
>>>> +        /* 1 available byte min or reads will block (we'll set non-blocking
>>>> +         * elsewhere, else we have to deal with read()=0 instead)
>>>> +         */
>>>> +        tio.c_cc[VMIN] = 1;
>>>> +        tio.c_cc[VTIME] = 0;
>>>> +        /* flush everything waiting for read/xmit, it's garbage at this point */
>>>> +        tcflush(fd, TCIFLUSH);
>>>> +        tcsetattr(fd, TCSANOW,&tio);
>>>> +        ret = conn_channel_add(s, fd);
>>>> +        if (ret) {
>>>> +            g_error("error adding channel to main loop");
>>>> +        }
>>>> +    } else if (strcmp(s->method, "unix-listen") == 0) {
>>>> +        fd = unix_listen(s->path, NULL, strlen(s->path));
>>>> +        if (fd == -1) {
>>>> +            g_critical("error opening path: %s", strerror(errno));
>>>> +            exit(EXIT_FAILURE);
>>>> +        }
>>>> +        ret = listen_channel_add(s, fd, true);
>>>> +        if (ret) {
>>>> +            g_critical("error binding/listening to specified socket");
>>>> +            exit(EXIT_FAILURE);
>>>> +        }
>>>> +    } else {
>>>> +        g_critical("unsupported channel method/type: %s", s->method);
>>>> +        exit(EXIT_FAILURE);
>>>> +    }
>>>> +
>>>> +    json_message_parser_init(&s->parser, process_event);
>>>> +    s->main_loop = g_main_loop_new(NULL, false);
>>>> +}
>>>> +
>>>> +int main(int argc, char **argv)
>>>> +{
>>>> +    const char *sopt = "hVvdm:p:l:f:";
>>>> +    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
>>>> +    const struct option lopt[] = {
>>>> +        { "help", 0, NULL, 'h' },
>>>> +        { "version", 0, NULL, 'V' },
>>>> +        { "logfile", 0, NULL, 'l' },
>>>> +        { "pidfile", 0, NULL, 'f' },
>>>> +        { "verbose", 0, NULL, 'v' },
>>>> +        { "method", 0, NULL, 'm' },
>>>> +        { "path", 0, NULL, 'p' },
>>>> +        { "daemonize", 0, NULL, 'd' },
>>>> +        { NULL, 0, NULL, 0 }
>>>> +    };
>>>> +    int opt_ind = 0, ch, daemonize = 0;
>>>> +    GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
>>>> +    FILE *log_file = stderr;
>>>> +    GAState *s;
>>>> +
>>>> +    while ((ch = getopt_long(argc, argv, sopt, lopt,&opt_ind)) != -1) {
>>>> +        switch (ch) {
>>>> +        case 'm':
>>>> +            method = optarg;
>>>> +            break;
>>>> +        case 'p':
>>>> +            path = optarg;
>>>> +            break;
>>>> +        case 'l':
>>>> +            log_file = fopen(optarg, "a");
>>>> +            if (!log_file) {
>>>> +                g_critical("unable to open specified log file: %s",
>>>> +                           strerror(errno));
>>>> +                return EXIT_FAILURE;
>>>> +            }
>>>> +            break;
>>>> +        case 'f':
>>>> +            pidfile = optarg;
>>>> +            break;
>>>> +        case 'v':
>>>> +            /* enable all log levels */
>>>> +            log_level = G_LOG_LEVEL_MASK;
>>>> +            break;
>>>> +        case 'V':
>>>> +            printf("QEMU Guest Agent %s\n", QGA_VERSION);
>>>> +            return 0;
>>>> +        case 'd':
>>>> +            daemonize = 1;
>>>> +            break;
>>>> +        case 'h':
>>>> +            usage(argv[0]);
>>>> +            return 0;
>>>> +        case '?':
>>>> +            g_print("Unknown option, try '%s --help' for more information.\n",
>>>> +                    argv[0]);
>>>> +            return EXIT_FAILURE;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (daemonize) {
>>>> +        g_debug("starting daemon");
>>>> +        become_daemon(pidfile);
>>>> +    }
>>>> +
>>>> +    g_type_init();
>>>> +    g_thread_init(NULL);
>>>> +
>>>> +    s = qemu_mallocz(sizeof(GAState));
>>>> +    s->conn_id = 0;
>>>> +    s->conn_channel = NULL;
>>>> +    s->path = path;
>>>> +    s->method = method;
>>>> +    s->log_file = log_file;
>>>> +    s->log_level = log_level;
>>>> +    g_log_set_default_handler(ga_log, s);
>>>> +    g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
>>>> +    s->logging_enabled = true;
>>>> +    ga_state = s;
>>>> +
>>>> +    module_call_init(MODULE_INIT_QAPI);
>>>> +    init_guest_agent(ga_state);
>>>> +    register_signal_handlers();
>>>> +
>>>> +    g_main_loop_run(ga_state->main_loop);
>>>> +
>>>> +    unlink(pidfile);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
>>>> index 688f120..66d1729 100644
>>>> --- a/qga/guest-agent-core.h
>>>> +++ b/qga/guest-agent-core.h
>>>> @@ -15,6 +15,7 @@
>>>>
>>>>    #define QGA_VERSION "1.0"
>>>>
>>>> +typedef struct GAState GAState;
>>>>    typedef struct GACommandState GACommandState;
>>>>
>>>>    void ga_command_state_add(GACommandState *cs,
>>>> @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
>>>>    void ga_command_state_init_all(GACommandState *cs);
>>>>    void ga_command_state_cleanup_all(GACommandState *cs);
>>>>    GACommandState *ga_command_state_new(void);
>>>> +bool ga_logging_enabled(GAState *s);
>>>> +void ga_disable_logging(GAState *s);
>>>> +void ga_enable_logging(GAState *s);
>>>
>>
>

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

* Re: [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-20 20:01         ` Michael Roth
@ 2011-07-20 20:05           ` Michael Roth
  2011-07-20 20:12             ` Luiz Capitulino
  0 siblings, 1 reply; 30+ messages in thread
From: Michael Roth @ 2011-07-20 20:05 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: aliguori, agl, qemu-devel, Jes.Sorensen

On 07/20/2011 03:01 PM, Michael Roth wrote:
> On 07/20/2011 02:31 PM, Luiz Capitulino wrote:
>> On Wed, 20 Jul 2011 14:25:06 -0500
>> Michael Roth<mdroth@linux.vnet.ibm.com> wrote:
>>
>>> On 07/20/2011 12:56 PM, Luiz Capitulino wrote:
>>>> On Tue, 19 Jul 2011 15:41:54 -0500
>>>> Michael Roth<mdroth@linux.vnet.ibm.com> wrote:
>>>>
>>>>> This is the actual guest daemon, it listens for requests over a
>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>> to dispatch routines, and writes the results back to the channel in
>>>>> a manner similar to QMP.
>>>>>
>>>>> A shorthand invocation:
>>>>>
>>>>> qemu-ga -d
>>>>>
>>>>> Is equivalent to:
>>>>>
>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>> -f /var/run/qemu-ga.pid -d
>>>>>
>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>> ---
>>>>> Makefile | 8 +-
>>>>> configure | 1 +
>>>>> qemu-ga.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>> qga/guest-agent-core.h | 4 +
>>>>> 4 files changed, 677 insertions(+), 3 deletions(-)
>>>>> create mode 100644 qemu-ga.c
>>>>>
>>>>> diff --git a/Makefile b/Makefile
>>>>> index b8cdf0e..0d2e33d 100644
>>>>> --- a/Makefile
>>>>> +++ b/Makefile
>>>>> @@ -140,7 +140,7 @@ endif
>>>>> ######################################################################
>>>>>
>>>>> qemu-img.o: qemu-img-cmds.h
>>>>> -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o:
>>>>> $(GENERATED_HEADERS)
>>>>> +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o:
>>>>> $(GENERATED_HEADERS)
>>>>>
>>>>> qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o
>>>>> $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y)
>>>>> $(version-obj-y) qemu-timer-common.o
>>>>>
>>>>> @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o
>>>>> qfloat.o qint.o qdict.o qstring.o qlist.o
>>>>>
>>>>> QGALIB=qga/guest-agent-command-state.o
>>>>>
>>>>> +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o
>>>>> error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y)
>>>>> $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o
>>>>> qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
>>>>> +
>>>>> QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
>>>>>
>>>>> clean:
>>>>> # avoid old build problems by removing potentially incorrect old files
>>>>> rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h
>>>>> opc-arm.h gen-op-arm.h
>>>>> rm -f qemu-options.def
>>>>> - rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
>>>>> + rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
>>>>> rm -Rf .libs
>>>>> rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d
>>>>> net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d
>>>>> qga/*.o qga/*.d
>>>>> rm -f qemu-img-cmds.h
>>>>> @@ -386,4 +388,4 @@ tarbin:
>>>>> $(mandir)/man8/qemu-nbd.8
>>>>>
>>>>> # Include automatically generated dependency files
>>>>> --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d
>>>>> ui/*.d qapi/*.d)
>>>>> +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d
>>>>> ui/*.d qapi/*.d qga/*.d)
>>>>> diff --git a/configure b/configure
>>>>> index 6a03002..445f674 100755
>>>>> --- a/configure
>>>>> +++ b/configure
>>>>> @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
>>>>> tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
>>>>> if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
>>>>> tools="qemu-nbd\$(EXESUF) $tools"
>>>>> + tools="qemu-ga\$(EXESUF) $tools"
>>>>> if [ "$check_utests" = "yes" ]; then
>>>>> tools="check-qint check-qstring check-qdict check-qlist $tools"
>>>>> tools="check-qfloat check-qjson $tools"
>>>>> diff --git a/qemu-ga.c b/qemu-ga.c
>>>>> new file mode 100644
>>>>> index 0000000..eb09100
>>>>> --- /dev/null
>>>>> +++ b/qemu-ga.c
>>>>> @@ -0,0 +1,667 @@
>>>>> +/*
>>>>> + * QEMU Guest Agent
>>>>> + *
>>>>> + * Copyright IBM Corp. 2011
>>>>> + *
>>>>> + * Authors:
>>>>> + * Adam Litke<aglitke@linux.vnet.ibm.com>
>>>>> + * Michael Roth<mdroth@linux.vnet.ibm.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<stdlib.h>
>>>>> +#include<stdio.h>
>>>>> +#include<stdbool.h>
>>>>> +#include<glib.h>
>>>>> +#include<gio/gio.h>
>>>>> +#include<getopt.h>
>>>>> +#include<termios.h>
>>>>> +#include<syslog.h>
>>>>> +#include "qemu_socket.h"
>>>>> +#include "json-streamer.h"
>>>>> +#include "json-parser.h"
>>>>> +#include "qint.h"
>>>>> +#include "qjson.h"
>>>>> +#include "qga/guest-agent-core.h"
>>>>> +#include "module.h"
>>>>> +#include "signal.h"
>>>>> +#include "qerror.h"
>>>>> +#include "error_int.h"
>>>>> +
>>>>> +#define QGA_VIRTIO_PATH_DEFAULT
>>>>> "/dev/virtio-ports/org.qemu.guest_agent.0"
>>>>> +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
>>>>> +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
>>>>> +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
>>>>> +
>>>>> +struct GAState {
>>>>> + JSONMessageParser parser;
>>>>> + GMainLoop *main_loop;
>>>>> + guint conn_id;
>>>>> + GSocket *conn_sock;
>>>>> + GIOChannel *conn_channel;
>>>>> + guint listen_id;
>>>>> + GSocket *listen_sock;
>>>>> + GIOChannel *listen_channel;
>>>>> + const char *path;
>>>>> + const char *method;
>>>>> + bool virtio; /* fastpath to check for virtio to deal with poll()
>>>>> quirks */
>>>>> + GACommandState *command_state;
>>>>> + GLogLevelFlags log_level;
>>>>> + FILE *log_file;
>>>>> + bool logging_enabled;
>>>>> +};
>>>>> +
>>>>> +static struct GAState *ga_state;
>>>>> +
>>>>> +static void quit_handler(int sig)
>>>>> +{
>>>>> + g_debug("recieved signal num %d, quitting", sig);
>>>>> +
>>>>> + if (g_main_loop_is_running(ga_state->main_loop)) {
>>>>> + g_main_loop_quit(ga_state->main_loop);
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void register_signal_handlers(void)
>>>>> +{
>>>>> + struct sigaction sigact;
>>>>> + int ret;
>>>>> +
>>>>> + memset(&sigact, 0, sizeof(struct sigaction));
>>>>> + sigact.sa_handler = quit_handler;
>>>>> +
>>>>> + ret = sigaction(SIGINT,&sigact, NULL);
>>>>> + if (ret == -1) {
>>>>> + g_error("error configuring signal handler: %s", strerror(errno));
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + ret = sigaction(SIGTERM,&sigact, NULL);
>>>>> + if (ret == -1) {
>>>>> + g_error("error configuring signal handler: %s", strerror(errno));
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void usage(const char *cmd)
>>>>> +{
>>>>> + printf(
>>>>> +"Usage: %s -c<channel_opts>\n"
>>>>> +"QEMU Guest Agent %s\n"
>>>>> +"\n"
>>>>> +" -m, --method transport method: one of unix-listen,
>>>>> virtio-serial, or\n"
>>>>> +" isa-serial (virtio-serial is the default)\n"
>>>>> +" -p, --path device/socket path (%s is the default for
>>>>> virtio-serial)\n"
>>>>> +" -l, --logfile set logfile path, logs to stderr by default\n"
>>>>> +" -f, --pidfile specify pidfile (default is %s)\n"
>>>>> +" -v, --verbose log extra debugging information\n"
>>>>> +" -V, --version print version information and exit\n"
>>>>> +" -d, --daemonize become a daemon\n"
>>>>> +" -h, --help display this help and exit\n"
>>>>> +"\n"
>>>>> +"Report bugs to<mdroth@linux.vnet.ibm.com>\n"
>>>>> + , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
>>>>> +}
>>>>> +
>>>>> +static void conn_channel_close(GAState *s);
>>>>> +
>>>>> +static const char *ga_log_level_str(GLogLevelFlags level)
>>>>> +{
>>>>> + switch (level& G_LOG_LEVEL_MASK) {
>>>>> + case G_LOG_LEVEL_ERROR:
>>>>> + return "error";
>>>>> + case G_LOG_LEVEL_CRITICAL:
>>>>> + return "critical";
>>>>> + case G_LOG_LEVEL_WARNING:
>>>>> + return "warning";
>>>>> + case G_LOG_LEVEL_MESSAGE:
>>>>> + return "message";
>>>>> + case G_LOG_LEVEL_INFO:
>>>>> + return "info";
>>>>> + case G_LOG_LEVEL_DEBUG:
>>>>> + return "debug";
>>>>> + default:
>>>>> + return "user";
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +bool ga_logging_enabled(GAState *s)
>>>>> +{
>>>>> + return s->logging_enabled;
>>>>> +}
>>>>> +
>>>>> +void ga_disable_logging(GAState *s)
>>>>> +{
>>>>> + s->logging_enabled = false;
>>>>> +}
>>>>> +
>>>>> +void ga_enable_logging(GAState *s)
>>>>> +{
>>>>> + s->logging_enabled = true;
>>>>> +}
>>>>> +
>>>>> +static void ga_log(const gchar *domain, GLogLevelFlags level,
>>>>> + const gchar *msg, gpointer opaque)
>>>>> +{
>>>>> + GAState *s = opaque;
>>>>> + GTimeVal time;
>>>>> + const char *level_str = ga_log_level_str(level);
>>>>> +
>>>>> + if (!ga_logging_enabled(s)) {
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + level&= G_LOG_LEVEL_MASK;
>>>>> + if (g_strcmp0(domain, "syslog") == 0) {
>>>>> + syslog(LOG_INFO, "%s: %s", level_str, msg);
>>>>> + } else if (level& s->log_level) {
>>>>> + g_get_current_time(&time);
>>>>> + fprintf(s->log_file,
>>>>> + "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
>>>>> + fflush(s->log_file);
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void become_daemon(const char *pidfile)
>>>>> +{
>>>>> + pid_t pid, sid;
>>>>> + int pidfd;
>>>>> + char *pidstr = NULL;
>>>>> +
>>>>> + pid = fork();
>>>>> + if (pid< 0) {
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + if (pid> 0) {
>>>>> + exit(EXIT_SUCCESS);
>>>>> + }
>>>>> +
>>>>> + pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
>>>>> + if (pidfd == -1) {
>>>>> + g_critical("Cannot create pid file, %s", strerror(errno));
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> +
>>>>> + if (asprintf(&pidstr, "%d", getpid()) == -1) {
>>>>> + g_critical("Cannot allocate memory");
>>>>> + goto fail;
>>>>> + }
>>>>> + if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
>>>>> + free(pidstr);
>>>>> + g_critical("Failed to write pid file");
>>>>> + goto fail;
>>>>> + }
>>>>> +
>>>>> + umask(0);
>>>>> + sid = setsid();
>>>>> + if (sid< 0) {
>>>>> + goto fail;
>>>>> + }
>>>>> + if ((chdir("/"))< 0) {
>>>>> + goto fail;
>>>>> + }
>>>>> +
>>>>> + close(STDIN_FILENO);
>>>>> + close(STDOUT_FILENO);
>>>>> + close(STDERR_FILENO);
>>>>> + free(pidstr);
>>>>> + return;
>>>>> +
>>>>> +fail:
>>>>> + unlink(pidfile);
>>>>> + g_critical("failed to daemonize");
>>>>> + exit(EXIT_FAILURE);
>>>>> +}
>>>>> +
>>>>> +static int conn_channel_send_buf(GIOChannel *channel, const char
>>>>> *buf,
>>>>> + gsize count)
>>>>> +{
>>>>> + GError *err = NULL;
>>>>> + gsize written = 0;
>>>>> + GIOStatus status;
>>>>> +
>>>>> + while (count) {
>>>>> + status = g_io_channel_write_chars(channel, buf,
>>>>> count,&written,&err);
>>>>> + g_debug("sending data, count: %d", (int)count);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error sending newline: %s", err->message);
>>>>> + return err->code;
>>>>> + }
>>>>> + if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
>>>>> + return -EPIPE;
>>>>> + }
>>>>> +
>>>>> + if (status == G_IO_STATUS_NORMAL) {
>>>>> + count -= written;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int conn_channel_send_payload(GIOChannel *channel, QObject
>>>>> *payload)
>>>>> +{
>>>>> + int ret = 0;
>>>>> + const char *buf;
>>>>> + QString *payload_qstr;
>>>>> + GError *err = NULL;
>>>>> +
>>>>> + g_assert(payload&& channel);
>>>>> +
>>>>> + payload_qstr = qobject_to_json(payload);
>>>>> + if (!payload_qstr) {
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + qstring_append_chr(payload_qstr, '\n');
>>>>> + buf = qstring_get_str(payload_qstr);
>>>>> + ret = conn_channel_send_buf(channel, buf, strlen(buf));
>>>>> + if (ret) {
>>>>> + goto out_free;
>>>>> + }
>>>>> +
>>>>> + g_io_channel_flush(channel,&err);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error flushing payload: %s", err->message);
>>>>> + ret = err->code;
>>>>> + goto out_free;
>>>>> + }
>>>>> +
>>>>> +out_free:
>>>>> + QDECREF(payload_qstr);
>>>>> + if (err) {
>>>>> + g_error_free(err);
>>>>> + }
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static void process_command(GAState *s, QDict *req)
>>>>> +{
>>>>> + QObject *rsp = NULL;
>>>>> + int ret;
>>>>> +
>>>>> + g_assert(req);
>>>>> + g_debug("processing command");
>>>>> + rsp = qmp_dispatch(QOBJECT(req));
>>>>> + if (rsp) {
>>>>> + ret = conn_channel_send_payload(s->conn_channel, rsp);
>>>>> + if (ret) {
>>>>> + g_warning("error sending payload: %s", strerror(ret));
>>>>> + }
>>>>> + qobject_decref(rsp);
>>>>> + } else {
>>>>> + g_warning("error getting response");
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +/* handle requests/control events coming in over the channel */
>>>>> +static void process_event(JSONMessageParser *parser, QList *tokens)
>>>>> +{
>>>>> + GAState *s = container_of(parser, GAState, parser);
>>>>> + QObject *obj;
>>>>> + QDict *qdict;
>>>>> + Error *err = NULL;
>>>>> + int ret;
>>>>> +
>>>>> + g_assert(s&& parser);
>>>>> +
>>>>> + g_debug("process_event: called");
>>>>> + obj = json_parser_parse_err(tokens, NULL,&err);
>>>>> + if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
>>>>> + qobject_decref(obj);
>>>>> + qdict = qdict_new();
>>>>> + if (!err) {
>>>>> + g_warning("failed to parse event: unknown error");
>>>>> + error_set(&err, QERR_JSON_PARSING);
>>>>> + } else {
>>>>> + g_warning("failed to parse event: %s", error_get_pretty(err));
>>>>> + }
>>>>> + qdict_put_obj(qdict, "error", error_get_qobject(err));
>>>>> + error_free(err);
>>>>> + } else {
>>>>> + qdict = qobject_to_qdict(obj);
>>>>> + }
>>>>> +
>>>>> + g_assert(qdict);
>>>>> +
>>>>> + /* handle host->guest commands */
>>>>> + if (qdict_haskey(qdict, "execute")) {
>>>>> + process_command(s, qdict);
>>>>> + } else {
>>>>> + if (!qdict_haskey(qdict, "error")) {
>>>>> + QDECREF(qdict);
>>>>> + qdict = qdict_new();
>>>>> + g_warning("unrecognized payload format");
>>>>> + error_set(&err, QERR_UNSUPPORTED);
>>>>> + qdict_put_obj(qdict, "error", error_get_qobject(err));
>>>>> + error_free(err);
>>>>> + }
>>>>> + ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
>>>>> + if (ret) {
>>>>> + g_warning("error sending payload: %s", strerror(ret));
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + QDECREF(qdict);
>>>>> +}
>>>>> +
>>>>> +static gboolean conn_channel_read(GIOChannel *channel,
>>>>> GIOCondition condition,
>>>>> + gpointer data)
>>>>> +{
>>>>> + GAState *s = data;
>>>>> + gchar buf[1024];
>>>>> + gsize count;
>>>>> + GError *err = NULL;
>>>>> + memset(buf, 0, 1024);
>>>>> + GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
>>>>> +&count,&err);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error reading channel: %s", err->message);
>>>>> + conn_channel_close(s);
>>>>> + g_error_free(err);
>>>>> + return false;
>>>>> + }
>>>>> + switch (status) {
>>>>> + case G_IO_STATUS_ERROR:
>>>>> + g_warning("problem");
>>>>> + return false;
>>>>> + case G_IO_STATUS_NORMAL:
>>>>> + g_debug("read data, count: %d, data: %s", (int)count, buf);
>>>>> + json_message_parser_feed(&s->parser, (char *)buf, (int)count);
>>>>> + case G_IO_STATUS_AGAIN:
>>>>> + /* virtio causes us to spin here when no process is attached to
>>>>> + * host-side chardev. sleep a bit to mitigate this
>>>>> + */
>>>>> + if (s->virtio) {
>>>>> + usleep(100*1000);
>>>>> + }
>>>>> + return true;
>>>>> + case G_IO_STATUS_EOF:
>>>>> + g_debug("received EOF");
>>>>> + conn_channel_close(s);
>>>>> + if (s->virtio) {
>>>>> + return true;
>>>>> + }
>>>>> + return false;
>>>>> + default:
>>>>> + g_warning("unknown channel read status, closing");
>>>>> + conn_channel_close(s);
>>>>> + return false;
>>>>> + }
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>> +static int conn_channel_add(GAState *s, int fd)
>>>>> +{
>>>>> + GIOChannel *conn_channel;
>>>>> + guint conn_id;
>>>>> + GError *err = NULL;
>>>>> +
>>>>> + g_assert(s&& !s->conn_channel);
>>>>> + conn_channel = g_io_channel_unix_new(fd);
>>>>> + g_assert(conn_channel);
>>>>> + g_io_channel_set_encoding(conn_channel, NULL,&err);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error setting channel encoding to binary");
>>>>> + g_error_free(err);
>>>>> + return -1;
>>>>> + }
>>>>> + conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
>>>>> + conn_channel_read, s);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error adding io watch: %s", err->message);
>>>>> + g_error_free(err);
>>>>> + return -1;
>>>>> + }
>>>>> + s->conn_channel = conn_channel;
>>>>> + s->conn_id = conn_id;
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static gboolean listen_channel_accept(GIOChannel *channel,
>>>>> + GIOCondition condition, gpointer data)
>>>>> +{
>>>>> + GAState *s = data;
>>>>> + GError *err = NULL;
>>>>> + g_assert(channel != NULL);
>>>>> + int ret;
>>>>> + bool accepted = false;
>>>>> +
>>>>> + s->conn_sock = g_socket_accept(s->listen_sock, NULL,&err);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error converting fd to gsocket: %s", err->message);
>>>>> + g_error_free(err);
>>>>> + goto out;
>>>>> + }
>>>>> + ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
>>>>> + if (ret) {
>>>>> + g_warning("error setting up connection");
>>>>> + goto out;
>>>>> + }
>>>>> + accepted = true;
>>>>> +
>>>>> +out:
>>>>> + /* only accept 1 connection at a time */
>>>>> + return !accepted;
>>>>> +}
>>>>> +
>>>>> +/* start polling for readable events on listen fd, new==true
>>>>> + * indicates we should use the existing s->listen_channel
>>>>> + */
>>>>> +static int listen_channel_add(GAState *s, int listen_fd, bool new)
>>>>> +{
>>>>> + GError *err = NULL;
>>>>> + guint listen_id;
>>>>> +
>>>>> + if (new) {
>>>>> + s->listen_channel = g_io_channel_unix_new(listen_fd);
>>>>> + if (s->listen_sock) {
>>>>> + g_object_unref(s->listen_sock);
>>>>> + }
>>>>> + s->listen_sock = g_socket_new_from_fd(listen_fd,&err);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error converting fd to gsocket: %s", err->message);
>>>>> + g_error_free(err);
>>>>> + return -1;
>>>>> + }
>>>>> + }
>>>>> + listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
>>>>> + listen_channel_accept, s);
>>>>> + if (err != NULL) {
>>>>> + g_warning("error adding io watch: %s", err->message);
>>>>> + g_error_free(err);
>>>>> + return -1;
>>>>> + }
>>>>> + return 0;
>>>>
>>>> I'm getting the following error when building this on a F15 guest:
>>>>
>>>> qemu-ga.c: In function ‘listen_channel_add’:
>>>> qemu-ga.c:453:11: error: variable ‘listen_id’ set but not used
>>>> [-Werror=unused-but-set-variable]
>>>> cc1: all warnings being treated as errors
>>>>
>>>> g_io_add_watch() doesn't seem to return an error. So if the fix is
>>>> just to
>>>> drop 'listen_id' and the last if statement altogether I can do it
>>>> myself,
>>>> otherwise you can send me only this patch.
>>>>
>>>> The series looks good otherwise.
>>>>
>>>
>>> Doh, sorry for not catching this. I did the same thing with conn_id as
>>> well. I made a patch available here:
>>>
>>> http://repo.or.cz/w/qemu/mdroth.git/blobdiff/a8d38e5bb2d558918ac0bad1ee618472ae34fe7a..581922e148f37e8f5844eeb11257eef8e9302709:/qemu-ga.c
>>>
>>>
>>> I tested the patch on a 32-bit F15 guest and it seems to do the trick.
>>>
>>> If you'd like you can just pull from
>>>
>>> git://repo.or.cz/qemu/mdroth.git qga-dev
>>>
>>> Which is this series (set3-v8) plus the patch right now.
>>
>> Can you resend the patch to the list please? If it's just this patch
>> (ie. patch 3/4) that was rebased, then you can submit only it. Otherwise
>> would be better to submit a new version of the series.
>>
>
> Patch sent. It's just against 3/4
>

Or did I misunderstand, and you actually want the revised patch 3/4 with 
these changes pre-applied? If so let me know and I'll fire that off.

>>>
>>>>> +}
>>>>> +
>>>>> +/* cleanup state for closed connection/session, start accepting new
>>>>> + * connections if we're in listening mode
>>>>> + */
>>>>> +static void conn_channel_close(GAState *s)
>>>>> +{
>>>>> + if (strcmp(s->method, "unix-listen") == 0) {
>>>>> + g_io_channel_shutdown(s->conn_channel, true, NULL);
>>>>> + g_object_unref(s->conn_sock);
>>>>> + s->conn_sock = NULL;
>>>>> + listen_channel_add(s, 0, false);
>>>>> + } else if (strcmp(s->method, "virtio-serial") == 0) {
>>>>> + /* we spin on EOF for virtio-serial, so back off a bit. also,
>>>>> + * dont close the connection in this case, it'll resume normal
>>>>> + * operation when another process connects to host chardev
>>>>> + */
>>>>> + usleep(100*1000);
>>>>> + goto out_noclose;
>>>>> + }
>>>>> + g_io_channel_unref(s->conn_channel);
>>>>> + s->conn_channel = NULL;
>>>>> + s->conn_id = 0;
>>>>> +out_noclose:
>>>>> + return;
>>>>> +}
>>>>> +
>>>>> +static void init_guest_agent(GAState *s)
>>>>> +{
>>>>> + struct termios tio;
>>>>> + int ret, fd;
>>>>> +
>>>>> + if (s->method == NULL) {
>>>>> + /* try virtio-serial as our default */
>>>>> + s->method = "virtio-serial";
>>>>> + }
>>>>> +
>>>>> + if (s->path == NULL) {
>>>>> + if (strcmp(s->method, "virtio-serial") != 0) {
>>>>> + g_critical("must specify a path for this channel");
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + /* try the default path for the virtio-serial port */
>>>>> + s->path = QGA_VIRTIO_PATH_DEFAULT;
>>>>> + }
>>>>> +
>>>>> + if (strcmp(s->method, "virtio-serial") == 0) {
>>>>> + s->virtio = true;
>>>>> + fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
>>>>> + if (fd == -1) {
>>>>> + g_critical("error opening channel: %s", strerror(errno));
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + ret = conn_channel_add(s, fd);
>>>>> + if (ret) {
>>>>> + g_critical("error adding channel to main loop");
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + } else if (strcmp(s->method, "isa-serial") == 0) {
>>>>> + fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
>>>>> + if (fd == -1) {
>>>>> + g_critical("error opening channel: %s", strerror(errno));
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + tcgetattr(fd,&tio);
>>>>> + /* set up serial port for non-canonical, dumb byte streaming */
>>>>> + tio.c_iflag&= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
>>>>> + INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
>>>>> + IMAXBEL);
>>>>> + tio.c_oflag = 0;
>>>>> + tio.c_lflag = 0;
>>>>> + tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
>>>>> + /* 1 available byte min or reads will block (we'll set non-blocking
>>>>> + * elsewhere, else we have to deal with read()=0 instead)
>>>>> + */
>>>>> + tio.c_cc[VMIN] = 1;
>>>>> + tio.c_cc[VTIME] = 0;
>>>>> + /* flush everything waiting for read/xmit, it's garbage at this
>>>>> point */
>>>>> + tcflush(fd, TCIFLUSH);
>>>>> + tcsetattr(fd, TCSANOW,&tio);
>>>>> + ret = conn_channel_add(s, fd);
>>>>> + if (ret) {
>>>>> + g_error("error adding channel to main loop");
>>>>> + }
>>>>> + } else if (strcmp(s->method, "unix-listen") == 0) {
>>>>> + fd = unix_listen(s->path, NULL, strlen(s->path));
>>>>> + if (fd == -1) {
>>>>> + g_critical("error opening path: %s", strerror(errno));
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + ret = listen_channel_add(s, fd, true);
>>>>> + if (ret) {
>>>>> + g_critical("error binding/listening to specified socket");
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> + } else {
>>>>> + g_critical("unsupported channel method/type: %s", s->method);
>>>>> + exit(EXIT_FAILURE);
>>>>> + }
>>>>> +
>>>>> + json_message_parser_init(&s->parser, process_event);
>>>>> + s->main_loop = g_main_loop_new(NULL, false);
>>>>> +}
>>>>> +
>>>>> +int main(int argc, char **argv)
>>>>> +{
>>>>> + const char *sopt = "hVvdm:p:l:f:";
>>>>> + const char *method = NULL, *path = NULL, *pidfile =
>>>>> QGA_PIDFILE_DEFAULT;
>>>>> + const struct option lopt[] = {
>>>>> + { "help", 0, NULL, 'h' },
>>>>> + { "version", 0, NULL, 'V' },
>>>>> + { "logfile", 0, NULL, 'l' },
>>>>> + { "pidfile", 0, NULL, 'f' },
>>>>> + { "verbose", 0, NULL, 'v' },
>>>>> + { "method", 0, NULL, 'm' },
>>>>> + { "path", 0, NULL, 'p' },
>>>>> + { "daemonize", 0, NULL, 'd' },
>>>>> + { NULL, 0, NULL, 0 }
>>>>> + };
>>>>> + int opt_ind = 0, ch, daemonize = 0;
>>>>> + GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
>>>>> + FILE *log_file = stderr;
>>>>> + GAState *s;
>>>>> +
>>>>> + while ((ch = getopt_long(argc, argv, sopt, lopt,&opt_ind)) != -1) {
>>>>> + switch (ch) {
>>>>> + case 'm':
>>>>> + method = optarg;
>>>>> + break;
>>>>> + case 'p':
>>>>> + path = optarg;
>>>>> + break;
>>>>> + case 'l':
>>>>> + log_file = fopen(optarg, "a");
>>>>> + if (!log_file) {
>>>>> + g_critical("unable to open specified log file: %s",
>>>>> + strerror(errno));
>>>>> + return EXIT_FAILURE;
>>>>> + }
>>>>> + break;
>>>>> + case 'f':
>>>>> + pidfile = optarg;
>>>>> + break;
>>>>> + case 'v':
>>>>> + /* enable all log levels */
>>>>> + log_level = G_LOG_LEVEL_MASK;
>>>>> + break;
>>>>> + case 'V':
>>>>> + printf("QEMU Guest Agent %s\n", QGA_VERSION);
>>>>> + return 0;
>>>>> + case 'd':
>>>>> + daemonize = 1;
>>>>> + break;
>>>>> + case 'h':
>>>>> + usage(argv[0]);
>>>>> + return 0;
>>>>> + case '?':
>>>>> + g_print("Unknown option, try '%s --help' for more information.\n",
>>>>> + argv[0]);
>>>>> + return EXIT_FAILURE;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + if (daemonize) {
>>>>> + g_debug("starting daemon");
>>>>> + become_daemon(pidfile);
>>>>> + }
>>>>> +
>>>>> + g_type_init();
>>>>> + g_thread_init(NULL);
>>>>> +
>>>>> + s = qemu_mallocz(sizeof(GAState));
>>>>> + s->conn_id = 0;
>>>>> + s->conn_channel = NULL;
>>>>> + s->path = path;
>>>>> + s->method = method;
>>>>> + s->log_file = log_file;
>>>>> + s->log_level = log_level;
>>>>> + g_log_set_default_handler(ga_log, s);
>>>>> + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
>>>>> + s->logging_enabled = true;
>>>>> + ga_state = s;
>>>>> +
>>>>> + module_call_init(MODULE_INIT_QAPI);
>>>>> + init_guest_agent(ga_state);
>>>>> + register_signal_handlers();
>>>>> +
>>>>> + g_main_loop_run(ga_state->main_loop);
>>>>> +
>>>>> + unlink(pidfile);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
>>>>> index 688f120..66d1729 100644
>>>>> --- a/qga/guest-agent-core.h
>>>>> +++ b/qga/guest-agent-core.h
>>>>> @@ -15,6 +15,7 @@
>>>>>
>>>>> #define QGA_VERSION "1.0"
>>>>>
>>>>> +typedef struct GAState GAState;
>>>>> typedef struct GACommandState GACommandState;
>>>>>
>>>>> void ga_command_state_add(GACommandState *cs,
>>>>> @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
>>>>> void ga_command_state_init_all(GACommandState *cs);
>>>>> void ga_command_state_cleanup_all(GACommandState *cs);
>>>>> GACommandState *ga_command_state_new(void);
>>>>> +bool ga_logging_enabled(GAState *s);
>>>>> +void ga_disable_logging(GAState *s);
>>>>> +void ga_enable_logging(GAState *s);
>>>>
>>>
>>
>

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

* Re: [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon
  2011-07-20 20:05           ` Michael Roth
@ 2011-07-20 20:12             ` Luiz Capitulino
  0 siblings, 0 replies; 30+ messages in thread
From: Luiz Capitulino @ 2011-07-20 20:12 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, agl, qemu-devel, Jes.Sorensen

On Wed, 20 Jul 2011 15:05:39 -0500
Michael Roth <mdroth@linux.vnet.ibm.com> wrote:

> On 07/20/2011 03:01 PM, Michael Roth wrote:
> > On 07/20/2011 02:31 PM, Luiz Capitulino wrote:
> >> On Wed, 20 Jul 2011 14:25:06 -0500
> >> Michael Roth<mdroth@linux.vnet.ibm.com> wrote:
> >>
> >>> On 07/20/2011 12:56 PM, Luiz Capitulino wrote:
> >>>> On Tue, 19 Jul 2011 15:41:54 -0500
> >>>> Michael Roth<mdroth@linux.vnet.ibm.com> wrote:
> >>>>
> >>>>> This is the actual guest daemon, it listens for requests over a
> >>>>> virtio-serial/isa-serial/unix socket channel and routes them through
> >>>>> to dispatch routines, and writes the results back to the channel in
> >>>>> a manner similar to QMP.
> >>>>>
> >>>>> A shorthand invocation:
> >>>>>
> >>>>> qemu-ga -d
> >>>>>
> >>>>> Is equivalent to:
> >>>>>
> >>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
> >>>>> -f /var/run/qemu-ga.pid -d
> >>>>>
> >>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
> >>>>> ---
> >>>>> Makefile | 8 +-
> >>>>> configure | 1 +
> >>>>> qemu-ga.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>>>> qga/guest-agent-core.h | 4 +
> >>>>> 4 files changed, 677 insertions(+), 3 deletions(-)
> >>>>> create mode 100644 qemu-ga.c
> >>>>>
> >>>>> diff --git a/Makefile b/Makefile
> >>>>> index b8cdf0e..0d2e33d 100644
> >>>>> --- a/Makefile
> >>>>> +++ b/Makefile
> >>>>> @@ -140,7 +140,7 @@ endif
> >>>>> ######################################################################
> >>>>>
> >>>>> qemu-img.o: qemu-img-cmds.h
> >>>>> -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o:
> >>>>> $(GENERATED_HEADERS)
> >>>>> +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o:
> >>>>> $(GENERATED_HEADERS)
> >>>>>
> >>>>> qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o
> >>>>> $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y)
> >>>>> $(version-obj-y) qemu-timer-common.o
> >>>>>
> >>>>> @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o
> >>>>> qfloat.o qint.o qdict.o qstring.o qlist.o
> >>>>>
> >>>>> QGALIB=qga/guest-agent-command-state.o
> >>>>>
> >>>>> +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o
> >>>>> error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y)
> >>>>> $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o
> >>>>> qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
> >>>>> +
> >>>>> QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
> >>>>>
> >>>>> clean:
> >>>>> # avoid old build problems by removing potentially incorrect old files
> >>>>> rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h
> >>>>> opc-arm.h gen-op-arm.h
> >>>>> rm -f qemu-options.def
> >>>>> - rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
> >>>>> + rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
> >>>>> rm -Rf .libs
> >>>>> rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d
> >>>>> net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d
> >>>>> qga/*.o qga/*.d
> >>>>> rm -f qemu-img-cmds.h
> >>>>> @@ -386,4 +388,4 @@ tarbin:
> >>>>> $(mandir)/man8/qemu-nbd.8
> >>>>>
> >>>>> # Include automatically generated dependency files
> >>>>> --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d
> >>>>> ui/*.d qapi/*.d)
> >>>>> +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d
> >>>>> ui/*.d qapi/*.d qga/*.d)
> >>>>> diff --git a/configure b/configure
> >>>>> index 6a03002..445f674 100755
> >>>>> --- a/configure
> >>>>> +++ b/configure
> >>>>> @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
> >>>>> tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
> >>>>> if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
> >>>>> tools="qemu-nbd\$(EXESUF) $tools"
> >>>>> + tools="qemu-ga\$(EXESUF) $tools"
> >>>>> if [ "$check_utests" = "yes" ]; then
> >>>>> tools="check-qint check-qstring check-qdict check-qlist $tools"
> >>>>> tools="check-qfloat check-qjson $tools"
> >>>>> diff --git a/qemu-ga.c b/qemu-ga.c
> >>>>> new file mode 100644
> >>>>> index 0000000..eb09100
> >>>>> --- /dev/null
> >>>>> +++ b/qemu-ga.c
> >>>>> @@ -0,0 +1,667 @@
> >>>>> +/*
> >>>>> + * QEMU Guest Agent
> >>>>> + *
> >>>>> + * Copyright IBM Corp. 2011
> >>>>> + *
> >>>>> + * Authors:
> >>>>> + * Adam Litke<aglitke@linux.vnet.ibm.com>
> >>>>> + * Michael Roth<mdroth@linux.vnet.ibm.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<stdlib.h>
> >>>>> +#include<stdio.h>
> >>>>> +#include<stdbool.h>
> >>>>> +#include<glib.h>
> >>>>> +#include<gio/gio.h>
> >>>>> +#include<getopt.h>
> >>>>> +#include<termios.h>
> >>>>> +#include<syslog.h>
> >>>>> +#include "qemu_socket.h"
> >>>>> +#include "json-streamer.h"
> >>>>> +#include "json-parser.h"
> >>>>> +#include "qint.h"
> >>>>> +#include "qjson.h"
> >>>>> +#include "qga/guest-agent-core.h"
> >>>>> +#include "module.h"
> >>>>> +#include "signal.h"
> >>>>> +#include "qerror.h"
> >>>>> +#include "error_int.h"
> >>>>> +
> >>>>> +#define QGA_VIRTIO_PATH_DEFAULT
> >>>>> "/dev/virtio-ports/org.qemu.guest_agent.0"
> >>>>> +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
> >>>>> +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
> >>>>> +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
> >>>>> +
> >>>>> +struct GAState {
> >>>>> + JSONMessageParser parser;
> >>>>> + GMainLoop *main_loop;
> >>>>> + guint conn_id;
> >>>>> + GSocket *conn_sock;
> >>>>> + GIOChannel *conn_channel;
> >>>>> + guint listen_id;
> >>>>> + GSocket *listen_sock;
> >>>>> + GIOChannel *listen_channel;
> >>>>> + const char *path;
> >>>>> + const char *method;
> >>>>> + bool virtio; /* fastpath to check for virtio to deal with poll()
> >>>>> quirks */
> >>>>> + GACommandState *command_state;
> >>>>> + GLogLevelFlags log_level;
> >>>>> + FILE *log_file;
> >>>>> + bool logging_enabled;
> >>>>> +};
> >>>>> +
> >>>>> +static struct GAState *ga_state;
> >>>>> +
> >>>>> +static void quit_handler(int sig)
> >>>>> +{
> >>>>> + g_debug("recieved signal num %d, quitting", sig);
> >>>>> +
> >>>>> + if (g_main_loop_is_running(ga_state->main_loop)) {
> >>>>> + g_main_loop_quit(ga_state->main_loop);
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +static void register_signal_handlers(void)
> >>>>> +{
> >>>>> + struct sigaction sigact;
> >>>>> + int ret;
> >>>>> +
> >>>>> + memset(&sigact, 0, sizeof(struct sigaction));
> >>>>> + sigact.sa_handler = quit_handler;
> >>>>> +
> >>>>> + ret = sigaction(SIGINT,&sigact, NULL);
> >>>>> + if (ret == -1) {
> >>>>> + g_error("error configuring signal handler: %s", strerror(errno));
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + ret = sigaction(SIGTERM,&sigact, NULL);
> >>>>> + if (ret == -1) {
> >>>>> + g_error("error configuring signal handler: %s", strerror(errno));
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +static void usage(const char *cmd)
> >>>>> +{
> >>>>> + printf(
> >>>>> +"Usage: %s -c<channel_opts>\n"
> >>>>> +"QEMU Guest Agent %s\n"
> >>>>> +"\n"
> >>>>> +" -m, --method transport method: one of unix-listen,
> >>>>> virtio-serial, or\n"
> >>>>> +" isa-serial (virtio-serial is the default)\n"
> >>>>> +" -p, --path device/socket path (%s is the default for
> >>>>> virtio-serial)\n"
> >>>>> +" -l, --logfile set logfile path, logs to stderr by default\n"
> >>>>> +" -f, --pidfile specify pidfile (default is %s)\n"
> >>>>> +" -v, --verbose log extra debugging information\n"
> >>>>> +" -V, --version print version information and exit\n"
> >>>>> +" -d, --daemonize become a daemon\n"
> >>>>> +" -h, --help display this help and exit\n"
> >>>>> +"\n"
> >>>>> +"Report bugs to<mdroth@linux.vnet.ibm.com>\n"
> >>>>> + , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
> >>>>> +}
> >>>>> +
> >>>>> +static void conn_channel_close(GAState *s);
> >>>>> +
> >>>>> +static const char *ga_log_level_str(GLogLevelFlags level)
> >>>>> +{
> >>>>> + switch (level& G_LOG_LEVEL_MASK) {
> >>>>> + case G_LOG_LEVEL_ERROR:
> >>>>> + return "error";
> >>>>> + case G_LOG_LEVEL_CRITICAL:
> >>>>> + return "critical";
> >>>>> + case G_LOG_LEVEL_WARNING:
> >>>>> + return "warning";
> >>>>> + case G_LOG_LEVEL_MESSAGE:
> >>>>> + return "message";
> >>>>> + case G_LOG_LEVEL_INFO:
> >>>>> + return "info";
> >>>>> + case G_LOG_LEVEL_DEBUG:
> >>>>> + return "debug";
> >>>>> + default:
> >>>>> + return "user";
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +bool ga_logging_enabled(GAState *s)
> >>>>> +{
> >>>>> + return s->logging_enabled;
> >>>>> +}
> >>>>> +
> >>>>> +void ga_disable_logging(GAState *s)
> >>>>> +{
> >>>>> + s->logging_enabled = false;
> >>>>> +}
> >>>>> +
> >>>>> +void ga_enable_logging(GAState *s)
> >>>>> +{
> >>>>> + s->logging_enabled = true;
> >>>>> +}
> >>>>> +
> >>>>> +static void ga_log(const gchar *domain, GLogLevelFlags level,
> >>>>> + const gchar *msg, gpointer opaque)
> >>>>> +{
> >>>>> + GAState *s = opaque;
> >>>>> + GTimeVal time;
> >>>>> + const char *level_str = ga_log_level_str(level);
> >>>>> +
> >>>>> + if (!ga_logging_enabled(s)) {
> >>>>> + return;
> >>>>> + }
> >>>>> +
> >>>>> + level&= G_LOG_LEVEL_MASK;
> >>>>> + if (g_strcmp0(domain, "syslog") == 0) {
> >>>>> + syslog(LOG_INFO, "%s: %s", level_str, msg);
> >>>>> + } else if (level& s->log_level) {
> >>>>> + g_get_current_time(&time);
> >>>>> + fprintf(s->log_file,
> >>>>> + "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
> >>>>> + fflush(s->log_file);
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +static void become_daemon(const char *pidfile)
> >>>>> +{
> >>>>> + pid_t pid, sid;
> >>>>> + int pidfd;
> >>>>> + char *pidstr = NULL;
> >>>>> +
> >>>>> + pid = fork();
> >>>>> + if (pid< 0) {
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + if (pid> 0) {
> >>>>> + exit(EXIT_SUCCESS);
> >>>>> + }
> >>>>> +
> >>>>> + pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
> >>>>> + if (pidfd == -1) {
> >>>>> + g_critical("Cannot create pid file, %s", strerror(errno));
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> +
> >>>>> + if (asprintf(&pidstr, "%d", getpid()) == -1) {
> >>>>> + g_critical("Cannot allocate memory");
> >>>>> + goto fail;
> >>>>> + }
> >>>>> + if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
> >>>>> + free(pidstr);
> >>>>> + g_critical("Failed to write pid file");
> >>>>> + goto fail;
> >>>>> + }
> >>>>> +
> >>>>> + umask(0);
> >>>>> + sid = setsid();
> >>>>> + if (sid< 0) {
> >>>>> + goto fail;
> >>>>> + }
> >>>>> + if ((chdir("/"))< 0) {
> >>>>> + goto fail;
> >>>>> + }
> >>>>> +
> >>>>> + close(STDIN_FILENO);
> >>>>> + close(STDOUT_FILENO);
> >>>>> + close(STDERR_FILENO);
> >>>>> + free(pidstr);
> >>>>> + return;
> >>>>> +
> >>>>> +fail:
> >>>>> + unlink(pidfile);
> >>>>> + g_critical("failed to daemonize");
> >>>>> + exit(EXIT_FAILURE);
> >>>>> +}
> >>>>> +
> >>>>> +static int conn_channel_send_buf(GIOChannel *channel, const char
> >>>>> *buf,
> >>>>> + gsize count)
> >>>>> +{
> >>>>> + GError *err = NULL;
> >>>>> + gsize written = 0;
> >>>>> + GIOStatus status;
> >>>>> +
> >>>>> + while (count) {
> >>>>> + status = g_io_channel_write_chars(channel, buf,
> >>>>> count,&written,&err);
> >>>>> + g_debug("sending data, count: %d", (int)count);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error sending newline: %s", err->message);
> >>>>> + return err->code;
> >>>>> + }
> >>>>> + if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
> >>>>> + return -EPIPE;
> >>>>> + }
> >>>>> +
> >>>>> + if (status == G_IO_STATUS_NORMAL) {
> >>>>> + count -= written;
> >>>>> + }
> >>>>> + }
> >>>>> +
> >>>>> + return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static int conn_channel_send_payload(GIOChannel *channel, QObject
> >>>>> *payload)
> >>>>> +{
> >>>>> + int ret = 0;
> >>>>> + const char *buf;
> >>>>> + QString *payload_qstr;
> >>>>> + GError *err = NULL;
> >>>>> +
> >>>>> + g_assert(payload&& channel);
> >>>>> +
> >>>>> + payload_qstr = qobject_to_json(payload);
> >>>>> + if (!payload_qstr) {
> >>>>> + return -EINVAL;
> >>>>> + }
> >>>>> +
> >>>>> + qstring_append_chr(payload_qstr, '\n');
> >>>>> + buf = qstring_get_str(payload_qstr);
> >>>>> + ret = conn_channel_send_buf(channel, buf, strlen(buf));
> >>>>> + if (ret) {
> >>>>> + goto out_free;
> >>>>> + }
> >>>>> +
> >>>>> + g_io_channel_flush(channel,&err);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error flushing payload: %s", err->message);
> >>>>> + ret = err->code;
> >>>>> + goto out_free;
> >>>>> + }
> >>>>> +
> >>>>> +out_free:
> >>>>> + QDECREF(payload_qstr);
> >>>>> + if (err) {
> >>>>> + g_error_free(err);
> >>>>> + }
> >>>>> + return ret;
> >>>>> +}
> >>>>> +
> >>>>> +static void process_command(GAState *s, QDict *req)
> >>>>> +{
> >>>>> + QObject *rsp = NULL;
> >>>>> + int ret;
> >>>>> +
> >>>>> + g_assert(req);
> >>>>> + g_debug("processing command");
> >>>>> + rsp = qmp_dispatch(QOBJECT(req));
> >>>>> + if (rsp) {
> >>>>> + ret = conn_channel_send_payload(s->conn_channel, rsp);
> >>>>> + if (ret) {
> >>>>> + g_warning("error sending payload: %s", strerror(ret));
> >>>>> + }
> >>>>> + qobject_decref(rsp);
> >>>>> + } else {
> >>>>> + g_warning("error getting response");
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +/* handle requests/control events coming in over the channel */
> >>>>> +static void process_event(JSONMessageParser *parser, QList *tokens)
> >>>>> +{
> >>>>> + GAState *s = container_of(parser, GAState, parser);
> >>>>> + QObject *obj;
> >>>>> + QDict *qdict;
> >>>>> + Error *err = NULL;
> >>>>> + int ret;
> >>>>> +
> >>>>> + g_assert(s&& parser);
> >>>>> +
> >>>>> + g_debug("process_event: called");
> >>>>> + obj = json_parser_parse_err(tokens, NULL,&err);
> >>>>> + if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
> >>>>> + qobject_decref(obj);
> >>>>> + qdict = qdict_new();
> >>>>> + if (!err) {
> >>>>> + g_warning("failed to parse event: unknown error");
> >>>>> + error_set(&err, QERR_JSON_PARSING);
> >>>>> + } else {
> >>>>> + g_warning("failed to parse event: %s", error_get_pretty(err));
> >>>>> + }
> >>>>> + qdict_put_obj(qdict, "error", error_get_qobject(err));
> >>>>> + error_free(err);
> >>>>> + } else {
> >>>>> + qdict = qobject_to_qdict(obj);
> >>>>> + }
> >>>>> +
> >>>>> + g_assert(qdict);
> >>>>> +
> >>>>> + /* handle host->guest commands */
> >>>>> + if (qdict_haskey(qdict, "execute")) {
> >>>>> + process_command(s, qdict);
> >>>>> + } else {
> >>>>> + if (!qdict_haskey(qdict, "error")) {
> >>>>> + QDECREF(qdict);
> >>>>> + qdict = qdict_new();
> >>>>> + g_warning("unrecognized payload format");
> >>>>> + error_set(&err, QERR_UNSUPPORTED);
> >>>>> + qdict_put_obj(qdict, "error", error_get_qobject(err));
> >>>>> + error_free(err);
> >>>>> + }
> >>>>> + ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
> >>>>> + if (ret) {
> >>>>> + g_warning("error sending payload: %s", strerror(ret));
> >>>>> + }
> >>>>> + }
> >>>>> +
> >>>>> + QDECREF(qdict);
> >>>>> +}
> >>>>> +
> >>>>> +static gboolean conn_channel_read(GIOChannel *channel,
> >>>>> GIOCondition condition,
> >>>>> + gpointer data)
> >>>>> +{
> >>>>> + GAState *s = data;
> >>>>> + gchar buf[1024];
> >>>>> + gsize count;
> >>>>> + GError *err = NULL;
> >>>>> + memset(buf, 0, 1024);
> >>>>> + GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
> >>>>> +&count,&err);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error reading channel: %s", err->message);
> >>>>> + conn_channel_close(s);
> >>>>> + g_error_free(err);
> >>>>> + return false;
> >>>>> + }
> >>>>> + switch (status) {
> >>>>> + case G_IO_STATUS_ERROR:
> >>>>> + g_warning("problem");
> >>>>> + return false;
> >>>>> + case G_IO_STATUS_NORMAL:
> >>>>> + g_debug("read data, count: %d, data: %s", (int)count, buf);
> >>>>> + json_message_parser_feed(&s->parser, (char *)buf, (int)count);
> >>>>> + case G_IO_STATUS_AGAIN:
> >>>>> + /* virtio causes us to spin here when no process is attached to
> >>>>> + * host-side chardev. sleep a bit to mitigate this
> >>>>> + */
> >>>>> + if (s->virtio) {
> >>>>> + usleep(100*1000);
> >>>>> + }
> >>>>> + return true;
> >>>>> + case G_IO_STATUS_EOF:
> >>>>> + g_debug("received EOF");
> >>>>> + conn_channel_close(s);
> >>>>> + if (s->virtio) {
> >>>>> + return true;
> >>>>> + }
> >>>>> + return false;
> >>>>> + default:
> >>>>> + g_warning("unknown channel read status, closing");
> >>>>> + conn_channel_close(s);
> >>>>> + return false;
> >>>>> + }
> >>>>> + return true;
> >>>>> +}
> >>>>> +
> >>>>> +static int conn_channel_add(GAState *s, int fd)
> >>>>> +{
> >>>>> + GIOChannel *conn_channel;
> >>>>> + guint conn_id;
> >>>>> + GError *err = NULL;
> >>>>> +
> >>>>> + g_assert(s&& !s->conn_channel);
> >>>>> + conn_channel = g_io_channel_unix_new(fd);
> >>>>> + g_assert(conn_channel);
> >>>>> + g_io_channel_set_encoding(conn_channel, NULL,&err);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error setting channel encoding to binary");
> >>>>> + g_error_free(err);
> >>>>> + return -1;
> >>>>> + }
> >>>>> + conn_id = g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
> >>>>> + conn_channel_read, s);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error adding io watch: %s", err->message);
> >>>>> + g_error_free(err);
> >>>>> + return -1;
> >>>>> + }
> >>>>> + s->conn_channel = conn_channel;
> >>>>> + s->conn_id = conn_id;
> >>>>> + return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static gboolean listen_channel_accept(GIOChannel *channel,
> >>>>> + GIOCondition condition, gpointer data)
> >>>>> +{
> >>>>> + GAState *s = data;
> >>>>> + GError *err = NULL;
> >>>>> + g_assert(channel != NULL);
> >>>>> + int ret;
> >>>>> + bool accepted = false;
> >>>>> +
> >>>>> + s->conn_sock = g_socket_accept(s->listen_sock, NULL,&err);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error converting fd to gsocket: %s", err->message);
> >>>>> + g_error_free(err);
> >>>>> + goto out;
> >>>>> + }
> >>>>> + ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
> >>>>> + if (ret) {
> >>>>> + g_warning("error setting up connection");
> >>>>> + goto out;
> >>>>> + }
> >>>>> + accepted = true;
> >>>>> +
> >>>>> +out:
> >>>>> + /* only accept 1 connection at a time */
> >>>>> + return !accepted;
> >>>>> +}
> >>>>> +
> >>>>> +/* start polling for readable events on listen fd, new==true
> >>>>> + * indicates we should use the existing s->listen_channel
> >>>>> + */
> >>>>> +static int listen_channel_add(GAState *s, int listen_fd, bool new)
> >>>>> +{
> >>>>> + GError *err = NULL;
> >>>>> + guint listen_id;
> >>>>> +
> >>>>> + if (new) {
> >>>>> + s->listen_channel = g_io_channel_unix_new(listen_fd);
> >>>>> + if (s->listen_sock) {
> >>>>> + g_object_unref(s->listen_sock);
> >>>>> + }
> >>>>> + s->listen_sock = g_socket_new_from_fd(listen_fd,&err);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error converting fd to gsocket: %s", err->message);
> >>>>> + g_error_free(err);
> >>>>> + return -1;
> >>>>> + }
> >>>>> + }
> >>>>> + listen_id = g_io_add_watch(s->listen_channel, G_IO_IN,
> >>>>> + listen_channel_accept, s);
> >>>>> + if (err != NULL) {
> >>>>> + g_warning("error adding io watch: %s", err->message);
> >>>>> + g_error_free(err);
> >>>>> + return -1;
> >>>>> + }
> >>>>> + return 0;
> >>>>
> >>>> I'm getting the following error when building this on a F15 guest:
> >>>>
> >>>> qemu-ga.c: In function ‘listen_channel_add’:
> >>>> qemu-ga.c:453:11: error: variable ‘listen_id’ set but not used
> >>>> [-Werror=unused-but-set-variable]
> >>>> cc1: all warnings being treated as errors
> >>>>
> >>>> g_io_add_watch() doesn't seem to return an error. So if the fix is
> >>>> just to
> >>>> drop 'listen_id' and the last if statement altogether I can do it
> >>>> myself,
> >>>> otherwise you can send me only this patch.
> >>>>
> >>>> The series looks good otherwise.
> >>>>
> >>>
> >>> Doh, sorry for not catching this. I did the same thing with conn_id as
> >>> well. I made a patch available here:
> >>>
> >>> http://repo.or.cz/w/qemu/mdroth.git/blobdiff/a8d38e5bb2d558918ac0bad1ee618472ae34fe7a..581922e148f37e8f5844eeb11257eef8e9302709:/qemu-ga.c
> >>>
> >>>
> >>> I tested the patch on a 32-bit F15 guest and it seems to do the trick.
> >>>
> >>> If you'd like you can just pull from
> >>>
> >>> git://repo.or.cz/qemu/mdroth.git qga-dev
> >>>
> >>> Which is this series (set3-v8) plus the patch right now.
> >>
> >> Can you resend the patch to the list please? If it's just this patch
> >> (ie. patch 3/4) that was rebased, then you can submit only it. Otherwise
> >> would be better to submit a new version of the series.
> >>
> >
> > Patch sent. It's just against 3/4
> >
> 
> Or did I misunderstand, and you actually want the revised patch 3/4 with 
> these changes pre-applied? If so let me know and I'll fire that off.

Yes, that would be better.

> 
> >>>
> >>>>> +}
> >>>>> +
> >>>>> +/* cleanup state for closed connection/session, start accepting new
> >>>>> + * connections if we're in listening mode
> >>>>> + */
> >>>>> +static void conn_channel_close(GAState *s)
> >>>>> +{
> >>>>> + if (strcmp(s->method, "unix-listen") == 0) {
> >>>>> + g_io_channel_shutdown(s->conn_channel, true, NULL);
> >>>>> + g_object_unref(s->conn_sock);
> >>>>> + s->conn_sock = NULL;
> >>>>> + listen_channel_add(s, 0, false);
> >>>>> + } else if (strcmp(s->method, "virtio-serial") == 0) {
> >>>>> + /* we spin on EOF for virtio-serial, so back off a bit. also,
> >>>>> + * dont close the connection in this case, it'll resume normal
> >>>>> + * operation when another process connects to host chardev
> >>>>> + */
> >>>>> + usleep(100*1000);
> >>>>> + goto out_noclose;
> >>>>> + }
> >>>>> + g_io_channel_unref(s->conn_channel);
> >>>>> + s->conn_channel = NULL;
> >>>>> + s->conn_id = 0;
> >>>>> +out_noclose:
> >>>>> + return;
> >>>>> +}
> >>>>> +
> >>>>> +static void init_guest_agent(GAState *s)
> >>>>> +{
> >>>>> + struct termios tio;
> >>>>> + int ret, fd;
> >>>>> +
> >>>>> + if (s->method == NULL) {
> >>>>> + /* try virtio-serial as our default */
> >>>>> + s->method = "virtio-serial";
> >>>>> + }
> >>>>> +
> >>>>> + if (s->path == NULL) {
> >>>>> + if (strcmp(s->method, "virtio-serial") != 0) {
> >>>>> + g_critical("must specify a path for this channel");
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + /* try the default path for the virtio-serial port */
> >>>>> + s->path = QGA_VIRTIO_PATH_DEFAULT;
> >>>>> + }
> >>>>> +
> >>>>> + if (strcmp(s->method, "virtio-serial") == 0) {
> >>>>> + s->virtio = true;
> >>>>> + fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
> >>>>> + if (fd == -1) {
> >>>>> + g_critical("error opening channel: %s", strerror(errno));
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + ret = conn_channel_add(s, fd);
> >>>>> + if (ret) {
> >>>>> + g_critical("error adding channel to main loop");
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + } else if (strcmp(s->method, "isa-serial") == 0) {
> >>>>> + fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
> >>>>> + if (fd == -1) {
> >>>>> + g_critical("error opening channel: %s", strerror(errno));
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + tcgetattr(fd,&tio);
> >>>>> + /* set up serial port for non-canonical, dumb byte streaming */
> >>>>> + tio.c_iflag&= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
> >>>>> + INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
> >>>>> + IMAXBEL);
> >>>>> + tio.c_oflag = 0;
> >>>>> + tio.c_lflag = 0;
> >>>>> + tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
> >>>>> + /* 1 available byte min or reads will block (we'll set non-blocking
> >>>>> + * elsewhere, else we have to deal with read()=0 instead)
> >>>>> + */
> >>>>> + tio.c_cc[VMIN] = 1;
> >>>>> + tio.c_cc[VTIME] = 0;
> >>>>> + /* flush everything waiting for read/xmit, it's garbage at this
> >>>>> point */
> >>>>> + tcflush(fd, TCIFLUSH);
> >>>>> + tcsetattr(fd, TCSANOW,&tio);
> >>>>> + ret = conn_channel_add(s, fd);
> >>>>> + if (ret) {
> >>>>> + g_error("error adding channel to main loop");
> >>>>> + }
> >>>>> + } else if (strcmp(s->method, "unix-listen") == 0) {
> >>>>> + fd = unix_listen(s->path, NULL, strlen(s->path));
> >>>>> + if (fd == -1) {
> >>>>> + g_critical("error opening path: %s", strerror(errno));
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + ret = listen_channel_add(s, fd, true);
> >>>>> + if (ret) {
> >>>>> + g_critical("error binding/listening to specified socket");
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> + } else {
> >>>>> + g_critical("unsupported channel method/type: %s", s->method);
> >>>>> + exit(EXIT_FAILURE);
> >>>>> + }
> >>>>> +
> >>>>> + json_message_parser_init(&s->parser, process_event);
> >>>>> + s->main_loop = g_main_loop_new(NULL, false);
> >>>>> +}
> >>>>> +
> >>>>> +int main(int argc, char **argv)
> >>>>> +{
> >>>>> + const char *sopt = "hVvdm:p:l:f:";
> >>>>> + const char *method = NULL, *path = NULL, *pidfile =
> >>>>> QGA_PIDFILE_DEFAULT;
> >>>>> + const struct option lopt[] = {
> >>>>> + { "help", 0, NULL, 'h' },
> >>>>> + { "version", 0, NULL, 'V' },
> >>>>> + { "logfile", 0, NULL, 'l' },
> >>>>> + { "pidfile", 0, NULL, 'f' },
> >>>>> + { "verbose", 0, NULL, 'v' },
> >>>>> + { "method", 0, NULL, 'm' },
> >>>>> + { "path", 0, NULL, 'p' },
> >>>>> + { "daemonize", 0, NULL, 'd' },
> >>>>> + { NULL, 0, NULL, 0 }
> >>>>> + };
> >>>>> + int opt_ind = 0, ch, daemonize = 0;
> >>>>> + GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
> >>>>> + FILE *log_file = stderr;
> >>>>> + GAState *s;
> >>>>> +
> >>>>> + while ((ch = getopt_long(argc, argv, sopt, lopt,&opt_ind)) != -1) {
> >>>>> + switch (ch) {
> >>>>> + case 'm':
> >>>>> + method = optarg;
> >>>>> + break;
> >>>>> + case 'p':
> >>>>> + path = optarg;
> >>>>> + break;
> >>>>> + case 'l':
> >>>>> + log_file = fopen(optarg, "a");
> >>>>> + if (!log_file) {
> >>>>> + g_critical("unable to open specified log file: %s",
> >>>>> + strerror(errno));
> >>>>> + return EXIT_FAILURE;
> >>>>> + }
> >>>>> + break;
> >>>>> + case 'f':
> >>>>> + pidfile = optarg;
> >>>>> + break;
> >>>>> + case 'v':
> >>>>> + /* enable all log levels */
> >>>>> + log_level = G_LOG_LEVEL_MASK;
> >>>>> + break;
> >>>>> + case 'V':
> >>>>> + printf("QEMU Guest Agent %s\n", QGA_VERSION);
> >>>>> + return 0;
> >>>>> + case 'd':
> >>>>> + daemonize = 1;
> >>>>> + break;
> >>>>> + case 'h':
> >>>>> + usage(argv[0]);
> >>>>> + return 0;
> >>>>> + case '?':
> >>>>> + g_print("Unknown option, try '%s --help' for more information.\n",
> >>>>> + argv[0]);
> >>>>> + return EXIT_FAILURE;
> >>>>> + }
> >>>>> + }
> >>>>> +
> >>>>> + if (daemonize) {
> >>>>> + g_debug("starting daemon");
> >>>>> + become_daemon(pidfile);
> >>>>> + }
> >>>>> +
> >>>>> + g_type_init();
> >>>>> + g_thread_init(NULL);
> >>>>> +
> >>>>> + s = qemu_mallocz(sizeof(GAState));
> >>>>> + s->conn_id = 0;
> >>>>> + s->conn_channel = NULL;
> >>>>> + s->path = path;
> >>>>> + s->method = method;
> >>>>> + s->log_file = log_file;
> >>>>> + s->log_level = log_level;
> >>>>> + g_log_set_default_handler(ga_log, s);
> >>>>> + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
> >>>>> + s->logging_enabled = true;
> >>>>> + ga_state = s;
> >>>>> +
> >>>>> + module_call_init(MODULE_INIT_QAPI);
> >>>>> + init_guest_agent(ga_state);
> >>>>> + register_signal_handlers();
> >>>>> +
> >>>>> + g_main_loop_run(ga_state->main_loop);
> >>>>> +
> >>>>> + unlink(pidfile);
> >>>>> +
> >>>>> + return 0;
> >>>>> +}
> >>>>> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
> >>>>> index 688f120..66d1729 100644
> >>>>> --- a/qga/guest-agent-core.h
> >>>>> +++ b/qga/guest-agent-core.h
> >>>>> @@ -15,6 +15,7 @@
> >>>>>
> >>>>> #define QGA_VERSION "1.0"
> >>>>>
> >>>>> +typedef struct GAState GAState;
> >>>>> typedef struct GACommandState GACommandState;
> >>>>>
> >>>>> void ga_command_state_add(GACommandState *cs,
> >>>>> @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
> >>>>> void ga_command_state_init_all(GACommandState *cs);
> >>>>> void ga_command_state_cleanup_all(GACommandState *cs);
> >>>>> GACommandState *ga_command_state_new(void);
> >>>>> +bool ga_logging_enabled(GAState *s);
> >>>>> +void ga_disable_logging(GAState *s);
> >>>>> +void ga_enable_logging(GAState *s);
> >>>>
> >>>
> >>
> >
> 

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

* [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon Michael Roth
  2011-07-20 17:56   ` Luiz Capitulino
  2011-07-20 20:00   ` [Qemu-devel] [PATCH] guest agent: qemu-ga, remove unused variables Michael Roth
@ 2011-07-20 20:19   ` Michael Roth
  2011-07-23 10:07     ` Alexander Graf
  2 siblings, 1 reply; 30+ messages in thread
From: Michael Roth @ 2011-07-20 20:19 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, Jes.Sorensen, agl, mdroth, lcapitulino

This is the actual guest daemon, it listens for requests over a
virtio-serial/isa-serial/unix socket channel and routes them through
to dispatch routines, and writes the results back to the channel in
a manner similar to QMP.

A shorthand invocation:

  qemu-ga -d

Is equivalent to:

  qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
          -f /var/run/qemu-ga.pid -d

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile               |    8 +-
 configure              |    1 +
 qemu-ga.c              |  650 ++++++++++++++++++++++++++++++++++++++++++++++++
 qga/guest-agent-core.h |    4 +
 4 files changed, 660 insertions(+), 3 deletions(-)
 create mode 100644 qemu-ga.c

diff --git a/Makefile b/Makefile
index b8cdf0e..0d2e33d 100644
--- a/Makefile
+++ b/Makefile
@@ -140,7 +140,7 @@ endif
 ######################################################################
 
 qemu-img.o: qemu-img-cmds.h
-qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
+qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
 
 qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
 
@@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o
 
 QGALIB=qga/guest-agent-command-state.o
 
+qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o
+
 QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
 
 clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 	rm -f qemu-options.def
-	rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
+	rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
 	rm -Rf .libs
 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
 	rm -f qemu-img-cmds.h
@@ -386,4 +388,4 @@ tarbin:
 	$(mandir)/man8/qemu-nbd.8
 
 # Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
diff --git a/configure b/configure
index 6a03002..445f674 100755
--- a/configure
+++ b/configure
@@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then
   tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
   if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
       tools="qemu-nbd\$(EXESUF) $tools"
+      tools="qemu-ga\$(EXESUF) $tools"
     if [ "$check_utests" = "yes" ]; then
       tools="check-qint check-qstring check-qdict check-qlist $tools"
       tools="check-qfloat check-qjson $tools"
diff --git a/qemu-ga.c b/qemu-ga.c
new file mode 100644
index 0000000..1f3585c
--- /dev/null
+++ b/qemu-ga.c
@@ -0,0 +1,650 @@
+/*
+ * QEMU Guest Agent
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ *  Adam Litke        <aglitke@linux.vnet.ibm.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.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 <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <getopt.h>
+#include <termios.h>
+#include <syslog.h>
+#include "qemu_socket.h"
+#include "json-streamer.h"
+#include "json-parser.h"
+#include "qint.h"
+#include "qjson.h"
+#include "qga/guest-agent-core.h"
+#include "module.h"
+#include "signal.h"
+#include "qerror.h"
+#include "error_int.h"
+
+#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
+#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
+#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
+#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
+
+struct GAState {
+    JSONMessageParser parser;
+    GMainLoop *main_loop;
+    GSocket *conn_sock;
+    GIOChannel *conn_channel;
+    GSocket *listen_sock;
+    GIOChannel *listen_channel;
+    const char *path;
+    const char *method;
+    bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
+    GACommandState *command_state;
+    GLogLevelFlags log_level;
+    FILE *log_file;
+    bool logging_enabled;
+};
+
+static struct GAState *ga_state;
+
+static void quit_handler(int sig)
+{
+    g_debug("recieved signal num %d, quitting", sig);
+
+    if (g_main_loop_is_running(ga_state->main_loop)) {
+        g_main_loop_quit(ga_state->main_loop);
+    }
+}
+
+static void register_signal_handlers(void)
+{
+    struct sigaction sigact;
+    int ret;
+
+    memset(&sigact, 0, sizeof(struct sigaction));
+    sigact.sa_handler = quit_handler;
+
+    ret = sigaction(SIGINT, &sigact, NULL);
+    if (ret == -1) {
+        g_error("error configuring signal handler: %s", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    ret = sigaction(SIGTERM, &sigact, NULL);
+    if (ret == -1) {
+        g_error("error configuring signal handler: %s", strerror(errno));
+    }
+}
+
+static void usage(const char *cmd)
+{
+    printf(
+"Usage: %s -c <channel_opts>\n"
+"QEMU Guest Agent %s\n"
+"\n"
+"  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
+"                    isa-serial (virtio-serial is the default)\n"
+"  -p, --path        device/socket path (%s is the default for virtio-serial)\n"
+"  -l, --logfile     set logfile path, logs to stderr by default\n"
+"  -f, --pidfile     specify pidfile (default is %s)\n"
+"  -v, --verbose     log extra debugging information\n"
+"  -V, --version     print version information and exit\n"
+"  -d, --daemonize   become a daemon\n"
+"  -h, --help        display this help and exit\n"
+"\n"
+"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
+    , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
+}
+
+static void conn_channel_close(GAState *s);
+
+static const char *ga_log_level_str(GLogLevelFlags level)
+{
+    switch (level & G_LOG_LEVEL_MASK) {
+        case G_LOG_LEVEL_ERROR:
+            return "error";
+        case G_LOG_LEVEL_CRITICAL:
+            return "critical";
+        case G_LOG_LEVEL_WARNING:
+            return "warning";
+        case G_LOG_LEVEL_MESSAGE:
+            return "message";
+        case G_LOG_LEVEL_INFO:
+            return "info";
+        case G_LOG_LEVEL_DEBUG:
+            return "debug";
+        default:
+            return "user";
+    }
+}
+
+bool ga_logging_enabled(GAState *s)
+{
+    return s->logging_enabled;
+}
+
+void ga_disable_logging(GAState *s)
+{
+    s->logging_enabled = false;
+}
+
+void ga_enable_logging(GAState *s)
+{
+    s->logging_enabled = true;
+}
+
+static void ga_log(const gchar *domain, GLogLevelFlags level,
+                   const gchar *msg, gpointer opaque)
+{
+    GAState *s = opaque;
+    GTimeVal time;
+    const char *level_str = ga_log_level_str(level);
+
+    if (!ga_logging_enabled(s)) {
+        return;
+    }
+
+    level &= G_LOG_LEVEL_MASK;
+    if (g_strcmp0(domain, "syslog") == 0) {
+        syslog(LOG_INFO, "%s: %s", level_str, msg);
+    } else if (level & s->log_level) {
+        g_get_current_time(&time);
+        fprintf(s->log_file,
+                "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
+        fflush(s->log_file);
+    }
+}
+
+static void become_daemon(const char *pidfile)
+{
+    pid_t pid, sid;
+    int pidfd;
+    char *pidstr = NULL;
+
+    pid = fork();
+    if (pid < 0) {
+        exit(EXIT_FAILURE);
+    }
+    if (pid > 0) {
+        exit(EXIT_SUCCESS);
+    }
+
+    pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
+    if (pidfd == -1) {
+        g_critical("Cannot create pid file, %s", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    if (asprintf(&pidstr, "%d", getpid()) == -1) {
+        g_critical("Cannot allocate memory");
+        goto fail;
+    }
+    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+        free(pidstr);
+        g_critical("Failed to write pid file");
+        goto fail;
+    }
+
+    umask(0);
+    sid = setsid();
+    if (sid < 0) {
+        goto fail;
+    }
+    if ((chdir("/")) < 0) {
+        goto fail;
+    }
+
+    close(STDIN_FILENO);
+    close(STDOUT_FILENO);
+    close(STDERR_FILENO);
+    free(pidstr);
+    return;
+
+fail:
+    unlink(pidfile);
+    g_critical("failed to daemonize");
+    exit(EXIT_FAILURE);
+}
+
+static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
+                                 gsize count)
+{
+    GError *err = NULL;
+    gsize written = 0;
+    GIOStatus status;
+
+    while (count) {
+        status = g_io_channel_write_chars(channel, buf, count, &written, &err);
+        g_debug("sending data, count: %d", (int)count);
+        if (err != NULL) {
+            g_warning("error sending newline: %s", err->message);
+            return err->code;
+        }
+        if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
+            return -EPIPE;
+        }
+
+        if (status == G_IO_STATUS_NORMAL) {
+            count -= written;
+        }
+    }
+
+    return 0;
+}
+
+static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
+{
+    int ret = 0;
+    const char *buf;
+    QString *payload_qstr;
+    GError *err = NULL;
+
+    g_assert(payload && channel);
+
+    payload_qstr = qobject_to_json(payload);
+    if (!payload_qstr) {
+        return -EINVAL;
+    }
+
+    qstring_append_chr(payload_qstr, '\n');
+    buf = qstring_get_str(payload_qstr);
+    ret = conn_channel_send_buf(channel, buf, strlen(buf));
+    if (ret) {
+        goto out_free;
+    }
+
+    g_io_channel_flush(channel, &err);
+    if (err != NULL) {
+        g_warning("error flushing payload: %s", err->message);
+        ret = err->code;
+        goto out_free;
+    }
+
+out_free:
+    QDECREF(payload_qstr);
+    if (err) {
+        g_error_free(err);
+    }
+    return ret;
+}
+
+static void process_command(GAState *s, QDict *req)
+{
+    QObject *rsp = NULL;
+    int ret;
+
+    g_assert(req);
+    g_debug("processing command");
+    rsp = qmp_dispatch(QOBJECT(req));
+    if (rsp) {
+        ret = conn_channel_send_payload(s->conn_channel, rsp);
+        if (ret) {
+            g_warning("error sending payload: %s", strerror(ret));
+        }
+        qobject_decref(rsp);
+    } else {
+        g_warning("error getting response");
+    }
+}
+
+/* handle requests/control events coming in over the channel */
+static void process_event(JSONMessageParser *parser, QList *tokens)
+{
+    GAState *s = container_of(parser, GAState, parser);
+    QObject *obj;
+    QDict *qdict;
+    Error *err = NULL;
+    int ret;
+
+    g_assert(s && parser);
+
+    g_debug("process_event: called");
+    obj = json_parser_parse_err(tokens, NULL, &err);
+    if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
+        qobject_decref(obj);
+        qdict = qdict_new();
+        if (!err) {
+            g_warning("failed to parse event: unknown error");
+            error_set(&err, QERR_JSON_PARSING);
+        } else {
+            g_warning("failed to parse event: %s", error_get_pretty(err));
+        }
+        qdict_put_obj(qdict, "error", error_get_qobject(err));
+        error_free(err);
+    } else {
+        qdict = qobject_to_qdict(obj);
+    }
+
+    g_assert(qdict);
+
+    /* handle host->guest commands */
+    if (qdict_haskey(qdict, "execute")) {
+        process_command(s, qdict);
+    } else {
+        if (!qdict_haskey(qdict, "error")) {
+            QDECREF(qdict);
+            qdict = qdict_new();
+            g_warning("unrecognized payload format");
+            error_set(&err, QERR_UNSUPPORTED);
+            qdict_put_obj(qdict, "error", error_get_qobject(err));
+            error_free(err);
+        }
+        ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
+        if (ret) {
+            g_warning("error sending payload: %s", strerror(ret));
+        }
+    }
+
+    QDECREF(qdict);
+}
+
+static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
+                                  gpointer data)
+{
+    GAState *s = data;
+    gchar buf[1024];
+    gsize count;
+    GError *err = NULL;
+    memset(buf, 0, 1024);
+    GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
+                                               &count, &err);
+    if (err != NULL) {
+        g_warning("error reading channel: %s", err->message);
+        conn_channel_close(s);
+        g_error_free(err);
+        return false;
+    }
+    switch (status) {
+    case G_IO_STATUS_ERROR:
+        g_warning("problem");
+        return false;
+    case G_IO_STATUS_NORMAL:
+        g_debug("read data, count: %d, data: %s", (int)count, buf);
+        json_message_parser_feed(&s->parser, (char *)buf, (int)count);
+    case G_IO_STATUS_AGAIN:
+        /* virtio causes us to spin here when no process is attached to
+         * host-side chardev. sleep a bit to mitigate this
+         */
+        if (s->virtio) {
+            usleep(100*1000);
+        }
+        return true;
+    case G_IO_STATUS_EOF:
+        g_debug("received EOF");
+        conn_channel_close(s);
+        if (s->virtio) {
+            return true;
+        }
+        return false;
+    default:
+        g_warning("unknown channel read status, closing");
+        conn_channel_close(s);
+        return false;
+    }
+    return true;
+}
+
+static int conn_channel_add(GAState *s, int fd)
+{
+    GIOChannel *conn_channel;
+    GError *err = NULL;
+
+    g_assert(s && !s->conn_channel);
+    conn_channel = g_io_channel_unix_new(fd);
+    g_assert(conn_channel);
+    g_io_channel_set_encoding(conn_channel, NULL, &err);
+    if (err != NULL) {
+        g_warning("error setting channel encoding to binary");
+        g_error_free(err);
+        return -1;
+    }
+    g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
+                   conn_channel_read, s);
+    s->conn_channel = conn_channel;
+    return 0;
+}
+
+static gboolean listen_channel_accept(GIOChannel *channel,
+                                      GIOCondition condition, gpointer data)
+{
+    GAState *s = data;
+    GError *err = NULL;
+    g_assert(channel != NULL);
+    int ret;
+    bool accepted = false;
+
+    s->conn_sock = g_socket_accept(s->listen_sock, NULL, &err);
+    if (err != NULL) {
+        g_warning("error converting fd to gsocket: %s", err->message);
+        g_error_free(err);
+        goto out;
+    }
+    ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock));
+    if (ret) {
+        g_warning("error setting up connection");
+        goto out;
+    }
+    accepted = true;
+
+out:
+    /* only accept 1 connection at a time */
+    return !accepted;
+}
+
+/* start polling for readable events on listen fd, new==true
+ * indicates we should use the existing s->listen_channel
+ */
+static int listen_channel_add(GAState *s, int listen_fd, bool new)
+{
+    GError *err = NULL;
+
+    if (new) {
+        s->listen_channel = g_io_channel_unix_new(listen_fd);
+        if (s->listen_sock) {
+            g_object_unref(s->listen_sock);
+        }
+        s->listen_sock = g_socket_new_from_fd(listen_fd, &err);
+        if (err != NULL) {
+            g_warning("error converting fd to gsocket: %s", err->message);
+            g_error_free(err);
+            return -1;
+        }
+    }
+    g_io_add_watch(s->listen_channel, G_IO_IN,
+                   listen_channel_accept, s);
+    return 0;
+}
+
+/* cleanup state for closed connection/session, start accepting new
+ * connections if we're in listening mode
+ */
+static void conn_channel_close(GAState *s)
+{
+    if (strcmp(s->method, "unix-listen") == 0) {
+        g_io_channel_shutdown(s->conn_channel, true, NULL);
+        g_object_unref(s->conn_sock);
+        s->conn_sock = NULL;
+        listen_channel_add(s, 0, false);
+    } else if (strcmp(s->method, "virtio-serial") == 0) {
+        /* we spin on EOF for virtio-serial, so back off a bit. also,
+         * dont close the connection in this case, it'll resume normal
+         * operation when another process connects to host chardev
+         */
+        usleep(100*1000);
+        goto out_noclose;
+    }
+    g_io_channel_unref(s->conn_channel);
+    s->conn_channel = NULL;
+out_noclose:
+    return;
+}
+
+static void init_guest_agent(GAState *s)
+{
+    struct termios tio;
+    int ret, fd;
+
+    if (s->method == NULL) {
+        /* try virtio-serial as our default */
+        s->method = "virtio-serial";
+    }
+
+    if (s->path == NULL) {
+        if (strcmp(s->method, "virtio-serial") != 0) {
+            g_critical("must specify a path for this channel");
+            exit(EXIT_FAILURE);
+        }
+        /* try the default path for the virtio-serial port */
+        s->path = QGA_VIRTIO_PATH_DEFAULT;
+    }
+
+    if (strcmp(s->method, "virtio-serial") == 0) {
+        s->virtio = true;
+        fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
+        if (fd == -1) {
+            g_critical("error opening channel: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        ret = conn_channel_add(s, fd);
+        if (ret) {
+            g_critical("error adding channel to main loop");
+            exit(EXIT_FAILURE);
+        }
+    } else if (strcmp(s->method, "isa-serial") == 0) {
+        fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
+        if (fd == -1) {
+            g_critical("error opening channel: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        tcgetattr(fd, &tio);
+        /* set up serial port for non-canonical, dumb byte streaming */
+        tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
+                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
+                         IMAXBEL);
+        tio.c_oflag = 0;
+        tio.c_lflag = 0;
+        tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
+        /* 1 available byte min or reads will block (we'll set non-blocking
+         * elsewhere, else we have to deal with read()=0 instead)
+         */
+        tio.c_cc[VMIN] = 1;
+        tio.c_cc[VTIME] = 0;
+        /* flush everything waiting for read/xmit, it's garbage at this point */
+        tcflush(fd, TCIFLUSH);
+        tcsetattr(fd, TCSANOW, &tio);
+        ret = conn_channel_add(s, fd);
+        if (ret) {
+            g_error("error adding channel to main loop");
+        }
+    } else if (strcmp(s->method, "unix-listen") == 0) {
+        fd = unix_listen(s->path, NULL, strlen(s->path));
+        if (fd == -1) {
+            g_critical("error opening path: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        ret = listen_channel_add(s, fd, true);
+        if (ret) {
+            g_critical("error binding/listening to specified socket");
+            exit(EXIT_FAILURE);
+        }
+    } else {
+        g_critical("unsupported channel method/type: %s", s->method);
+        exit(EXIT_FAILURE);
+    }
+
+    json_message_parser_init(&s->parser, process_event);
+    s->main_loop = g_main_loop_new(NULL, false);
+}
+
+int main(int argc, char **argv)
+{
+    const char *sopt = "hVvdm:p:l:f:";
+    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
+    const struct option lopt[] = {
+        { "help", 0, NULL, 'h' },
+        { "version", 0, NULL, 'V' },
+        { "logfile", 0, NULL, 'l' },
+        { "pidfile", 0, NULL, 'f' },
+        { "verbose", 0, NULL, 'v' },
+        { "method", 0, NULL, 'm' },
+        { "path", 0, NULL, 'p' },
+        { "daemonize", 0, NULL, 'd' },
+        { NULL, 0, NULL, 0 }
+    };
+    int opt_ind = 0, ch, daemonize = 0;
+    GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
+    FILE *log_file = stderr;
+    GAState *s;
+
+    while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
+        switch (ch) {
+        case 'm':
+            method = optarg;
+            break;
+        case 'p':
+            path = optarg;
+            break;
+        case 'l':
+            log_file = fopen(optarg, "a");
+            if (!log_file) {
+                g_critical("unable to open specified log file: %s",
+                           strerror(errno));
+                return EXIT_FAILURE;
+            }
+            break;
+        case 'f':
+            pidfile = optarg;
+            break;
+        case 'v':
+            /* enable all log levels */
+            log_level = G_LOG_LEVEL_MASK;
+            break;
+        case 'V':
+            printf("QEMU Guest Agent %s\n", QGA_VERSION);
+            return 0;
+        case 'd':
+            daemonize = 1;
+            break;
+        case 'h':
+            usage(argv[0]);
+            return 0;
+        case '?':
+            g_print("Unknown option, try '%s --help' for more information.\n",
+                    argv[0]);
+            return EXIT_FAILURE;
+        }
+    }
+
+    if (daemonize) {
+        g_debug("starting daemon");
+        become_daemon(pidfile);
+    }
+
+    g_type_init();
+    g_thread_init(NULL);
+
+    s = qemu_mallocz(sizeof(GAState));
+    s->conn_channel = NULL;
+    s->path = path;
+    s->method = method;
+    s->log_file = log_file;
+    s->log_level = log_level;
+    g_log_set_default_handler(ga_log, s);
+    g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
+    s->logging_enabled = true;
+    ga_state = s;
+
+    module_call_init(MODULE_INIT_QAPI);
+    init_guest_agent(ga_state);
+    register_signal_handlers();
+
+    g_main_loop_run(ga_state->main_loop);
+
+    unlink(pidfile);
+
+    return 0;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index 688f120..66d1729 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -15,6 +15,7 @@
 
 #define QGA_VERSION "1.0"
 
+typedef struct GAState GAState;
 typedef struct GACommandState GACommandState;
 
 void ga_command_state_add(GACommandState *cs,
@@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs,
 void ga_command_state_init_all(GACommandState *cs);
 void ga_command_state_cleanup_all(GACommandState *cs);
 GACommandState *ga_command_state_new(void);
+bool ga_logging_enabled(GAState *s);
+void ga_disable_logging(GAState *s);
+void ga_enable_logging(GAState *s);
-- 
1.7.0.4

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-20 20:19   ` [Qemu-devel] [PATCH] guest agent: qemu-ga daemon Michael Roth
@ 2011-07-23 10:07     ` Alexander Graf
  2011-07-23 10:33       ` Alexander Graf
  2011-07-23 16:06       ` Michael Roth
  0 siblings, 2 replies; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 10:07 UTC (permalink / raw)
  To: Michael Roth
  Cc: agl, Jes Sorensen, qemu-devel@nongnu.org Developers,
	Luiz Capitulino, Blue Swirl, Anthony Liguori


On 20.07.2011, at 22:19, Michael Roth wrote:

> This is the actual guest daemon, it listens for requests over a
> virtio-serial/isa-serial/unix socket channel and routes them through
> to dispatch routines, and writes the results back to the channel in
> a manner similar to QMP.
> 
> A shorthand invocation:
> 
>  qemu-ga -d
> 
> Is equivalent to:
> 
>  qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>          -f /var/run/qemu-ga.pid -d
> 
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>

A rebase on top of current HEAD gave me the following on openSUSE 11.1 PPC:


agraf@lychee:/home/agraf/release/qemu> make
  CC    qemu-ga.o
qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
qemu-ga.c: In function ‘ga_logging_enabled’:
qemu-ga.c:127: error: ‘GAState’ has no member named ‘logging_enabled’
qemu-ga.c: In function ‘ga_disable_logging’:
qemu-ga.c:132: error: ‘GAState’ has no member named ‘logging_enabled’
qemu-ga.c: In function ‘ga_enable_logging’:
qemu-ga.c:137: error: ‘GAState’ has no member named ‘logging_enabled’
qemu-ga.c: In function ‘ga_log’:
qemu-ga.c:154: error: ‘GAState’ has no member named ‘log_level’
qemu-ga.c:156: error: ‘GAState’ has no member named ‘log_file’
qemu-ga.c:158: error: ‘GAState’ has no member named ‘log_file’
qemu-ga.c: In function ‘process_command’:
qemu-ga.c:284: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c: In function ‘process_event’:
qemu-ga.c:336: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c: In function ‘conn_channel_read’:
qemu-ga.c:372: error: ‘GAState’ has no member named ‘virtio’
qemu-ga.c:379: error: ‘GAState’ has no member named ‘virtio’
qemu-ga.c: In function ‘conn_channel_add’:
qemu-ga.c:396: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c:407: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c: In function ‘listen_channel_accept’:
qemu-ga.c:420: error: ‘GAState’ has no member named ‘conn_sock’
cc1: warnings being treated as errors
qemu-ga.c:420: error: implicit declaration of function ‘g_socket_accept’
qemu-ga.c:420: error: nested extern declaration of ‘g_socket_accept’
qemu-ga.c:420: error: ‘GAState’ has no member named ‘listen_sock’
qemu-ga.c:426: error: implicit declaration of function ‘g_socket_get_fd’
qemu-ga.c:426: error: nested extern declaration of ‘g_socket_get_fd’
qemu-ga.c:426: error: ‘GAState’ has no member named ‘conn_sock’
qemu-ga.c: In function ‘listen_channel_add’:
qemu-ga.c:446: error: ‘GAState’ has no member named ‘listen_channel’
qemu-ga.c:447: error: ‘GAState’ has no member named ‘listen_sock’
qemu-ga.c:448: error: ‘GAState’ has no member named ‘listen_sock’
qemu-ga.c:450: error: ‘GAState’ has no member named ‘listen_sock’
qemu-ga.c:450: error: implicit declaration of function ‘g_socket_new_from_fd’
qemu-ga.c:450: error: nested extern declaration of ‘g_socket_new_from_fd’
qemu-ga.c:457: error: ‘GAState’ has no member named ‘listen_channel’
qemu-ga.c: In function ‘conn_channel_close’:
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:468: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c:469: error: ‘GAState’ has no member named ‘conn_sock’
qemu-ga.c:470: error: ‘GAState’ has no member named ‘conn_sock’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:480: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c:481: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c: In function ‘init_guest_agent’:
qemu-ga.c:491: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:493: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:496: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:502: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:506: error: ‘GAState’ has no member named ‘virtio’
qemu-ga.c:507: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:518: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:544: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:544: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:555: error: ‘GAState’ has no member named ‘method’
qemu-ga.c: In function ‘main’:
qemu-ga.c:631: error: ‘GAState’ has no member named ‘conn_channel’
qemu-ga.c:632: error: ‘GAState’ has no member named ‘path’
qemu-ga.c:633: error: ‘GAState’ has no member named ‘method’
qemu-ga.c:634: error: ‘GAState’ has no member named ‘log_file’
qemu-ga.c:635: error: ‘GAState’ has no member named ‘log_level’
qemu-ga.c:638: error: ‘GAState’ has no member named ‘logging_enabled’
qemu-ga.c:639: error: ‘GAState’ has no member named ‘command_state’
qemu-ga.c:640: error: ‘GAState’ has no member named ‘command_state’
qemu-ga.c:641: error: ‘GAState’ has no member named ‘command_state’
qemu-ga.c:650: error: ‘struct GAState’ has no member named ‘command_state’
make: *** [qemu-ga.o] Error 1


... but it also breaks here:

qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first use in this function)
qga/guest-agent-commands.c:443: error: (Each undeclared identifier is reported only once
qga/guest-agent-commands.c:443: error: for each function it appears in.)
qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use in this function)


which I guess is fixed with Anthony's patch? Not sure. Either way, please make sure to add proper configure checks before relying on obscure functionality.


Alex

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 10:07     ` Alexander Graf
@ 2011-07-23 10:33       ` Alexander Graf
  2011-07-23 16:06       ` Michael Roth
  1 sibling, 0 replies; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 10:33 UTC (permalink / raw)
  To: Michael Roth
  Cc: agl, Jes Sorensen, qemu-devel@nongnu.org Developers,
	Luiz Capitulino, Blue Swirl, Anthony Liguori


On 23.07.2011, at 12:07, Alexander Graf wrote:

> 
> On 20.07.2011, at 22:19, Michael Roth wrote:
> 
>> This is the actual guest daemon, it listens for requests over a
>> virtio-serial/isa-serial/unix socket channel and routes them through
>> to dispatch routines, and writes the results back to the channel in
>> a manner similar to QMP.
>> 
>> A shorthand invocation:
>> 
>> qemu-ga -d
>> 
>> Is equivalent to:
>> 
>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>         -f /var/run/qemu-ga.pid -d
>> 
>> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> 
> A rebase on top of current HEAD gave me the following on openSUSE 11.1 PPC:
> 
> 
> agraf@lychee:/home/agraf/release/qemu> make
>  CC    qemu-ga.o
> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
> qemu-ga.c: In function ‘ga_logging_enabled’:
> qemu-ga.c:127: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c: In function ‘ga_disable_logging’:
> qemu-ga.c:132: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c: In function ‘ga_enable_logging’:
> qemu-ga.c:137: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c: In function ‘ga_log’:

[...]

> ... but it also breaks here:
> 
> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first use in this function)
> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is reported only once
> qga/guest-agent-commands.c:443: error: for each function it appears in.)
> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use in this function)
> 
> 
> which I guess is fixed with Anthony's patch? Not sure. Either way, please make sure to add proper configure checks before relying on obscure functionality.

JFYI: The following patch fixes it for me :). Why are we pulling in such a huge feature just before rc0 anyways? This should go in after the stable fork, so it gets some time to cook before people end up having to use it.


diff --git a/configure b/configure
index 6911c3b..156ae3c 100755
--- a/configure
+++ b/configure
@@ -2555,7 +2555,6 @@ if test "$softmmu" = yes ; then
   tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
   if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
       tools="qemu-nbd\$(EXESUF) $tools"
-      tools="qemu-ga\$(EXESUF) $tools"
     if [ "$check_utests" = "yes" ]; then
       tools="check-qint check-qstring check-qdict check-qlist $tools"
       tools="check-qfloat check-qjson $tools"


Alex

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 10:07     ` Alexander Graf
  2011-07-23 10:33       ` Alexander Graf
@ 2011-07-23 16:06       ` Michael Roth
  2011-07-23 16:10         ` Anthony Liguori
  2011-07-23 16:10         ` Anthony Liguori
  1 sibling, 2 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-23 16:06 UTC (permalink / raw)
  To: Alexander Graf
  Cc: agl, Jes Sorensen, qemu-devel@nongnu.org Developers,
	Luiz Capitulino, Blue Swirl, Anthony Liguori

On 07/23/2011 05:07 AM, Alexander Graf wrote:
>
> On 20.07.2011, at 22:19, Michael Roth wrote:
>
>> This is the actual guest daemon, it listens for requests over a
>> virtio-serial/isa-serial/unix socket channel and routes them through
>> to dispatch routines, and writes the results back to the channel in
>> a manner similar to QMP.
>>
>> A shorthand invocation:
>>
>>   qemu-ga -d
>>
>> Is equivalent to:
>>
>>   qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>           -f /var/run/qemu-ga.pid -d
>>
>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>
> A rebase on top of current HEAD gave me the following on openSUSE 11.1 PPC:
>
>
> agraf@lychee:/home/agraf/release/qemu>  make
>    CC    qemu-ga.o
> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
> qemu-ga.c: In function ‘ga_logging_enabled’:
> qemu-ga.c:127: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c: In function ‘ga_disable_logging’:
> qemu-ga.c:132: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c: In function ‘ga_enable_logging’:
> qemu-ga.c:137: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c: In function ‘ga_log’:
> qemu-ga.c:154: error: ‘GAState’ has no member named ‘log_level’
> qemu-ga.c:156: error: ‘GAState’ has no member named ‘log_file’
> qemu-ga.c:158: error: ‘GAState’ has no member named ‘log_file’
> qemu-ga.c: In function ‘process_command’:
> qemu-ga.c:284: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c: In function ‘process_event’:
> qemu-ga.c:336: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c: In function ‘conn_channel_read’:
> qemu-ga.c:372: error: ‘GAState’ has no member named ‘virtio’
> qemu-ga.c:379: error: ‘GAState’ has no member named ‘virtio’
> qemu-ga.c: In function ‘conn_channel_add’:
> qemu-ga.c:396: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c:407: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c: In function ‘listen_channel_accept’:
> qemu-ga.c:420: error: ‘GAState’ has no member named ‘conn_sock’
> cc1: warnings being treated as errors
> qemu-ga.c:420: error: implicit declaration of function ‘g_socket_accept’
> qemu-ga.c:420: error: nested extern declaration of ‘g_socket_accept’
> qemu-ga.c:420: error: ‘GAState’ has no member named ‘listen_sock’
> qemu-ga.c:426: error: implicit declaration of function ‘g_socket_get_fd’
> qemu-ga.c:426: error: nested extern declaration of ‘g_socket_get_fd’
> qemu-ga.c:426: error: ‘GAState’ has no member named ‘conn_sock’
> qemu-ga.c: In function ‘listen_channel_add’:
> qemu-ga.c:446: error: ‘GAState’ has no member named ‘listen_channel’
> qemu-ga.c:447: error: ‘GAState’ has no member named ‘listen_sock’
> qemu-ga.c:448: error: ‘GAState’ has no member named ‘listen_sock’
> qemu-ga.c:450: error: ‘GAState’ has no member named ‘listen_sock’
> qemu-ga.c:450: error: implicit declaration of function ‘g_socket_new_from_fd’
> qemu-ga.c:450: error: nested extern declaration of ‘g_socket_new_from_fd’
> qemu-ga.c:457: error: ‘GAState’ has no member named ‘listen_channel’
> qemu-ga.c: In function ‘conn_channel_close’:
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:467: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:468: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c:469: error: ‘GAState’ has no member named ‘conn_sock’
> qemu-ga.c:470: error: ‘GAState’ has no member named ‘conn_sock’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:472: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:480: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c:481: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c: In function ‘init_guest_agent’:
> qemu-ga.c:491: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:493: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:496: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:497: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:502: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:505: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:506: error: ‘GAState’ has no member named ‘virtio’
> qemu-ga.c:507: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:517: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:518: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:543: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:544: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:544: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:555: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c: In function ‘main’:
> qemu-ga.c:631: error: ‘GAState’ has no member named ‘conn_channel’
> qemu-ga.c:632: error: ‘GAState’ has no member named ‘path’
> qemu-ga.c:633: error: ‘GAState’ has no member named ‘method’
> qemu-ga.c:634: error: ‘GAState’ has no member named ‘log_file’
> qemu-ga.c:635: error: ‘GAState’ has no member named ‘log_level’
> qemu-ga.c:638: error: ‘GAState’ has no member named ‘logging_enabled’
> qemu-ga.c:639: error: ‘GAState’ has no member named ‘command_state’
> qemu-ga.c:640: error: ‘GAState’ has no member named ‘command_state’
> qemu-ga.c:641: error: ‘GAState’ has no member named ‘command_state’
> qemu-ga.c:650: error: ‘struct GAState’ has no member named ‘command_state’
> make: *** [qemu-ga.o] Error 1
>
>
> ... but it also breaks here:
>
> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first use in this function)
> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is reported only once
> qga/guest-agent-commands.c:443: error: for each function it appears in.)
> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use in this function)
>
>
> which I guess is fixed with Anthony's patch? Not sure. Either way, please make sure to add proper configure checks before relying on obscure functionality.
>

GSocket is part of gio and should've been pulled in via the gio 
pkg-config test. I'll poke around on a SLES11 guest and try to figure 
out what's going on here.

Anthony's patch fixed a build issue when trying to use fsfreeze on a 
non-linux host, so it wouldn't fix the issue you mentioned. I'll look 
into that as well.

>
> Alex
>

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:06       ` Michael Roth
@ 2011-07-23 16:10         ` Anthony Liguori
  2011-07-23 19:23           ` Jes Sorensen
  2011-07-23 16:10         ` Anthony Liguori
  1 sibling, 1 reply; 30+ messages in thread
From: Anthony Liguori @ 2011-07-23 16:10 UTC (permalink / raw)
  To: Michael Roth
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen,
	Alexander Graf, Luiz Capitulino, Blue Swirl, Anthony Liguori

On 07/23/2011 11:06 AM, Michael Roth wrote:
> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>
>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>
>>> This is the actual guest daemon, it listens for requests over a
>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>> to dispatch routines, and writes the results back to the channel in
>>> a manner similar to QMP.
>>>
>>> A shorthand invocation:
>>>
>>> qemu-ga -d
>>>
>>> Is equivalent to:
>>>
>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>> -f /var/run/qemu-ga.pid -d
>>>
>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>
>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>> PPC:
>>
>>
>> agraf@lychee:/home/agraf/release/qemu> make
>> CC qemu-ga.o
>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’

GIO is fairly new.  It may not be available on openSUSE.

Mike, you probably need to do a configure test for GIO and if it's not 
present, don't build qemu-ga.

Maybe look at just using GIOChannels which have been around much longer 
than GSocket.

>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
>> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first
>> use in this function)
>> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is
>> reported only once
>> qga/guest-agent-commands.c:443: error: for each function it appears in.)
>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
>> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use
>> in this function)

The kernel probably doesn't implement FIFREEZE.  You need to do a 
configure test and set CONFIG_FSFREEZE appropriately.  I anticipated 
this and that's why I added CONFIG_FSFREEZE and didn't just do __linux__.

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:06       ` Michael Roth
  2011-07-23 16:10         ` Anthony Liguori
@ 2011-07-23 16:10         ` Anthony Liguori
  2011-07-23 16:43           ` Michael Roth
  1 sibling, 1 reply; 30+ messages in thread
From: Anthony Liguori @ 2011-07-23 16:10 UTC (permalink / raw)
  To: Michael Roth
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen,
	Alexander Graf, Luiz Capitulino, Blue Swirl, Anthony Liguori

On 07/23/2011 11:06 AM, Michael Roth wrote:
> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>
>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>
>>> This is the actual guest daemon, it listens for requests over a
>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>> to dispatch routines, and writes the results back to the channel in
>>> a manner similar to QMP.
>>>
>>> A shorthand invocation:
>>>
>>> qemu-ga -d
>>>
>>> Is equivalent to:
>>>
>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>> -f /var/run/qemu-ga.pid -d
>>>
>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>
>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>> PPC:
>>
>>
>> agraf@lychee:/home/agraf/release/qemu> make
>> CC qemu-ga.o
>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’

GIO is fairly new.  It may not be available on openSUSE.

Mike, you probably need to do a configure test for GIO and if it's not 
present, don't build qemu-ga.

Maybe look at just using GIOChannels which have been around much longer 
than GSocket.

>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
>> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first
>> use in this function)
>> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is
>> reported only once
>> qga/guest-agent-commands.c:443: error: for each function it appears in.)
>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
>> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use
>> in this function)

The kernel probably doesn't implement FIFREEZE.  You need to do a 
configure test and set CONFIG_FSFREEZE appropriately.  I anticipated 
this and that's why I added CONFIG_FSFREEZE and didn't just do __linux__.

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:10         ` Anthony Liguori
@ 2011-07-23 16:43           ` Michael Roth
  2011-07-23 16:49             ` Anthony Liguori
  2011-07-23 18:34             ` Alexander Graf
  0 siblings, 2 replies; 30+ messages in thread
From: Michael Roth @ 2011-07-23 16:43 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen,
	Alexander Graf, Luiz Capitulino, Blue Swirl, Anthony Liguori

On 07/23/2011 11:10 AM, Anthony Liguori wrote:
> On 07/23/2011 11:06 AM, Michael Roth wrote:
>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>
>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>
>>>> This is the actual guest daemon, it listens for requests over a
>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>> to dispatch routines, and writes the results back to the channel in
>>>> a manner similar to QMP.
>>>>
>>>> A shorthand invocation:
>>>>
>>>> qemu-ga -d
>>>>
>>>> Is equivalent to:
>>>>
>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>> -f /var/run/qemu-ga.pid -d
>>>>
>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>
>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>> PPC:
>>>
>>>
>>> agraf@lychee:/home/agraf/release/qemu> make
>>> CC qemu-ga.o
>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>
> GIO is fairly new. It may not be available on openSUSE.
>
> Mike, you probably need to do a configure test for GIO and if it's not
> present, don't build qemu-ga.

It should've failed the glib probe in that case. I think we might need a 
compile test to catch this GSocket issue.

Rather than building qemu-ga when possible, should we just go ahead and 
add a configure option and only run the probes when it's set? At least 
until QMP/QEMU start formally using glib? If so, on or off by default?

>
> Maybe look at just using GIOChannels which have been around much longer
> than GSocket.
>

The GSocket stuff is being used in addition to GIOChannels to handle the 
listen/accept stuff. I believe we can drop it in favor of 
qemu-sockets.c/osdep.c though.

>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
>>> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first
>>> use in this function)
>>> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is
>>> reported only once
>>> qga/guest-agent-commands.c:443: error: for each function it appears in.)
>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
>>> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use
>>> in this function)
>
> The kernel probably doesn't implement FIFREEZE. You need to do a
> configure test and set CONFIG_FSFREEZE appropriately. I anticipated this
> and that's why I added CONFIG_FSFREEZE and didn't just do __linux__.
>

Will do, thanks.

> Regards,
>
> Anthony Liguori

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:43           ` Michael Roth
@ 2011-07-23 16:49             ` Anthony Liguori
  2011-07-23 18:35               ` Alexander Graf
  2011-07-23 18:34             ` Alexander Graf
  1 sibling, 1 reply; 30+ messages in thread
From: Anthony Liguori @ 2011-07-23 16:49 UTC (permalink / raw)
  To: Michael Roth
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen,
	Alexander Graf, Luiz Capitulino, Blue Swirl, Anthony Liguori

On 07/23/2011 11:43 AM, Michael Roth wrote:
> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>>
>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>>
>>>>> This is the actual guest daemon, it listens for requests over a
>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>> to dispatch routines, and writes the results back to the channel in
>>>>> a manner similar to QMP.
>>>>>
>>>>> A shorthand invocation:
>>>>>
>>>>> qemu-ga -d
>>>>>
>>>>> Is equivalent to:
>>>>>
>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>> -f /var/run/qemu-ga.pid -d
>>>>>
>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>
>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>> PPC:
>>>>
>>>>
>>>> agraf@lychee:/home/agraf/release/qemu> make
>>>> CC qemu-ga.o
>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>>
>> GIO is fairly new. It may not be available on openSUSE.
>>
>> Mike, you probably need to do a configure test for GIO and if it's not
>> present, don't build qemu-ga.
>
> It should've failed the glib probe in that case. I think we might need a
> compile test to catch this GSocket issue.

Indeed.  Alex, can you help debug this a bit?  We can tr to setup a SUSE 
system.

Can you confirm that gio is actually present?

Regards,

Anthony Liguori

>
> Rather than building qemu-ga when possible, should we just go ahead and
> add a configure option and only run the probes when it's set? At least
> until QMP/QEMU start formally using glib? If so, on or off by default?
>
>>
>> Maybe look at just using GIOChannels which have been around much longer
>> than GSocket.
>>
>
> The GSocket stuff is being used in addition to GIOChannels to handle the
> listen/accept stuff. I believe we can drop it in favor of
> qemu-sockets.c/osdep.c though.
>
>>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
>>>> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first
>>>> use in this function)
>>>> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is
>>>> reported only once
>>>> qga/guest-agent-commands.c:443: error: for each function it appears
>>>> in.)
>>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
>>>> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use
>>>> in this function)
>>
>> The kernel probably doesn't implement FIFREEZE. You need to do a
>> configure test and set CONFIG_FSFREEZE appropriately. I anticipated this
>> and that's why I added CONFIG_FSFREEZE and didn't just do __linux__.
>>
>
> Will do, thanks.
>
>> Regards,
>>
>> Anthony Liguori
>

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:43           ` Michael Roth
  2011-07-23 16:49             ` Anthony Liguori
@ 2011-07-23 18:34             ` Alexander Graf
  2011-07-23 19:14               ` Anthony Liguori
  1 sibling, 1 reply; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 18:34 UTC (permalink / raw)
  To: Michael Roth
  Cc: agl, Jes Sorensen, qemu-devel@nongnu.org Developers,
	Luiz Capitulino, Blue Swirl, Anthony Liguori


On 23.07.2011, at 18:43, Michael Roth wrote:

> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>> 
>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>> 
>>>>> This is the actual guest daemon, it listens for requests over a
>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>> to dispatch routines, and writes the results back to the channel in
>>>>> a manner similar to QMP.
>>>>> 
>>>>> A shorthand invocation:
>>>>> 
>>>>> qemu-ga -d
>>>>> 
>>>>> Is equivalent to:
>>>>> 
>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>> -f /var/run/qemu-ga.pid -d
>>>>> 
>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>> 
>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>> PPC:
>>>> 
>>>> 
>>>> agraf@lychee:/home/agraf/release/qemu> make
>>>> CC qemu-ga.o
>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>> 
>> GIO is fairly new. It may not be available on openSUSE.
>> 
>> Mike, you probably need to do a configure test for GIO and if it's not
>> present, don't build qemu-ga.
> 
> It should've failed the glib probe in that case. I think we might need a compile test to catch this GSocket issue.
> 
> Rather than building qemu-ga when possible, should we just go ahead and add a configure option and only run the probes when it's set? At least until QMP/QEMU start formally using glib? If so, on or off by default?

In general, I like the workflow of adding a feature with default off and then enabling it after it has been in for a couple of weeks. Since this got pushed so late for 0.15, I'd personally prefer to see it as preview (disabled by default) in 0.15 and only enabled by default if the requirements are there on 0.16.


Alex

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:49             ` Anthony Liguori
@ 2011-07-23 18:35               ` Alexander Graf
  2011-07-23 19:12                 ` Anthony Liguori
  0 siblings, 1 reply; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 18:35 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen, Michael Roth,
	Luiz Capitulino, Blue Swirl, Anthony Liguori


On 23.07.2011, at 18:49, Anthony Liguori wrote:

> On 07/23/2011 11:43 AM, Michael Roth wrote:
>> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>>> 
>>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>>> 
>>>>>> This is the actual guest daemon, it listens for requests over a
>>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>>> to dispatch routines, and writes the results back to the channel in
>>>>>> a manner similar to QMP.
>>>>>> 
>>>>>> A shorthand invocation:
>>>>>> 
>>>>>> qemu-ga -d
>>>>>> 
>>>>>> Is equivalent to:
>>>>>> 
>>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>>> -f /var/run/qemu-ga.pid -d
>>>>>> 
>>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>> 
>>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>>> PPC:
>>>>> 
>>>>> 
>>>>> agraf@lychee:/home/agraf/release/qemu> make
>>>>> CC qemu-ga.o
>>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>>> 
>>> GIO is fairly new. It may not be available on openSUSE.
>>> 
>>> Mike, you probably need to do a configure test for GIO and if it's not
>>> present, don't build qemu-ga.
>> 
>> It should've failed the glib probe in that case. I think we might need a
>> compile test to catch this GSocket issue.
> 
> Indeed.  Alex, can you help debug this a bit?  We can tr to setup a SUSE system.

It's not only about SUSE vs. non-SUSE. This was 11.1 (ancient, but latest ppc release) on PowerPC.

> Can you confirm that gio is actually present?

Sure, tell me how :). I'm fairly ignorant when it comes to g* stuff.


Alex

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 18:35               ` Alexander Graf
@ 2011-07-23 19:12                 ` Anthony Liguori
  2011-07-23 19:20                   ` Alexander Graf
  0 siblings, 1 reply; 30+ messages in thread
From: Anthony Liguori @ 2011-07-23 19:12 UTC (permalink / raw)
  To: Alexander Graf
  Cc: Anthony Liguori, agl, Michael Roth, Jes Sorensen,
	qemu-devel@nongnu.org Developers, Luiz Capitulino, Blue Swirl,
	Anthony Liguori

On 07/23/2011 01:35 PM, Alexander Graf wrote:
>
> On 23.07.2011, at 18:49, Anthony Liguori wrote:
>
>> On 07/23/2011 11:43 AM, Michael Roth wrote:
>>> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>>>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>>>>
>>>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>>>>
>>>>>>> This is the actual guest daemon, it listens for requests over a
>>>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>>>> to dispatch routines, and writes the results back to the channel in
>>>>>>> a manner similar to QMP.
>>>>>>>
>>>>>>> A shorthand invocation:
>>>>>>>
>>>>>>> qemu-ga -d
>>>>>>>
>>>>>>> Is equivalent to:
>>>>>>>
>>>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>>>> -f /var/run/qemu-ga.pid -d
>>>>>>>
>>>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>>>
>>>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>>>> PPC:
>>>>>>
>>>>>>
>>>>>> agraf@lychee:/home/agraf/release/qemu>  make
>>>>>> CC qemu-ga.o
>>>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>>>>
>>>> GIO is fairly new. It may not be available on openSUSE.
>>>>
>>>> Mike, you probably need to do a configure test for GIO and if it's not
>>>> present, don't build qemu-ga.
>>>
>>> It should've failed the glib probe in that case. I think we might need a
>>> compile test to catch this GSocket issue.
>>
>> Indeed.  Alex, can you help debug this a bit?  We can tr to setup a SUSE system.
>
> It's not only about SUSE vs. non-SUSE. This was 11.1 (ancient, but latest ppc release) on PowerPC.
>
>> Can you confirm that gio is actually present?
>
> Sure, tell me how :). I'm fairly ignorant when it comes to g* stuff.

pkg-config --modversion gio-2.0

Regards,

Anthony Liguori

>
> Alex
>
>

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 18:34             ` Alexander Graf
@ 2011-07-23 19:14               ` Anthony Liguori
  2011-07-23 19:22                 ` Alexander Graf
  0 siblings, 1 reply; 30+ messages in thread
From: Anthony Liguori @ 2011-07-23 19:14 UTC (permalink / raw)
  To: Alexander Graf
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen, Michael Roth,
	Luiz Capitulino, Blue Swirl, Anthony Liguori

On 07/23/2011 01:34 PM, Alexander Graf wrote:
>
> On 23.07.2011, at 18:43, Michael Roth wrote:
>
>> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>>>
>>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>>>
>>>>>> This is the actual guest daemon, it listens for requests over a
>>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>>> to dispatch routines, and writes the results back to the channel in
>>>>>> a manner similar to QMP.
>>>>>>
>>>>>> A shorthand invocation:
>>>>>>
>>>>>> qemu-ga -d
>>>>>>
>>>>>> Is equivalent to:
>>>>>>
>>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>>> -f /var/run/qemu-ga.pid -d
>>>>>>
>>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>>
>>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>>> PPC:
>>>>>
>>>>>
>>>>> agraf@lychee:/home/agraf/release/qemu>  make
>>>>> CC qemu-ga.o
>>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>>>
>>> GIO is fairly new. It may not be available on openSUSE.
>>>
>>> Mike, you probably need to do a configure test for GIO and if it's not
>>> present, don't build qemu-ga.
>>
>> It should've failed the glib probe in that case. I think we might need a compile test to catch this GSocket issue.
>>
>> Rather than building qemu-ga when possible, should we just go ahead and add a configure option and only run the probes when it's set? At least until QMP/QEMU start formally using glib? If so, on or off by default?
>
> In general, I like the workflow of adding a feature with default off and then enabling it after it has been in for a couple of weeks. Since this got pushed so late for 0.15, I'd personally prefer to see it as preview (disabled by default) in 0.15 and only enabled by default if the requirements are there on 0.16.

The only way something like this gets tested is to default it on.

We default off'd the I/O thread even after years we still don't have it 
enabled.

With respect to 0.15, this bit of code is totally isolated from 
everything else.  Worst case scenario, we just disable it on platforms 
where it doesn't work.  It presents no real risk to the stability of the 
release.

Regards,

Anthony Liguori

>
>
> Alex
>
>

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 19:12                 ` Anthony Liguori
@ 2011-07-23 19:20                   ` Alexander Graf
  0 siblings, 0 replies; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 19:20 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Anthony Liguori, agl, Michael Roth, Jes Sorensen,
	qemu-devel@nongnu.org Developers, Luiz Capitulino, Blue Swirl,
	Anthony Liguori


On 23.07.2011, at 21:12, Anthony Liguori wrote:

> On 07/23/2011 01:35 PM, Alexander Graf wrote:
>> 
>> On 23.07.2011, at 18:49, Anthony Liguori wrote:
>> 
>>> On 07/23/2011 11:43 AM, Michael Roth wrote:
>>>> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>>>>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>>>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>>>>> 
>>>>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>>>>> 
>>>>>>>> This is the actual guest daemon, it listens for requests over a
>>>>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>>>>> to dispatch routines, and writes the results back to the channel in
>>>>>>>> a manner similar to QMP.
>>>>>>>> 
>>>>>>>> A shorthand invocation:
>>>>>>>> 
>>>>>>>> qemu-ga -d
>>>>>>>> 
>>>>>>>> Is equivalent to:
>>>>>>>> 
>>>>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>>>>> -f /var/run/qemu-ga.pid -d
>>>>>>>> 
>>>>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>>>> 
>>>>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>>>>> PPC:
>>>>>>> 
>>>>>>> 
>>>>>>> agraf@lychee:/home/agraf/release/qemu>  make
>>>>>>> CC qemu-ga.o
>>>>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>>>>> 
>>>>> GIO is fairly new. It may not be available on openSUSE.
>>>>> 
>>>>> Mike, you probably need to do a configure test for GIO and if it's not
>>>>> present, don't build qemu-ga.
>>>> 
>>>> It should've failed the glib probe in that case. I think we might need a
>>>> compile test to catch this GSocket issue.
>>> 
>>> Indeed.  Alex, can you help debug this a bit?  We can tr to setup a SUSE system.
>> 
>> It's not only about SUSE vs. non-SUSE. This was 11.1 (ancient, but latest ppc release) on PowerPC.
>> 
>>> Can you confirm that gio is actually present?
>> 
>> Sure, tell me how :). I'm fairly ignorant when it comes to g* stuff.
> 
> pkg-config --modversion gio-2.0


agraf@lychee:~> pkg-config --modversion gio-2.0
2.18.2


Alex

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 19:14               ` Anthony Liguori
@ 2011-07-23 19:22                 ` Alexander Graf
  2011-07-23 21:12                   ` Anthony Liguori
  0 siblings, 1 reply; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 19:22 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen, Michael Roth,
	Luiz Capitulino, Blue Swirl, Anthony Liguori


On 23.07.2011, at 21:14, Anthony Liguori wrote:

> On 07/23/2011 01:34 PM, Alexander Graf wrote:
>> 
>> On 23.07.2011, at 18:43, Michael Roth wrote:
>> 
>>> On 07/23/2011 11:10 AM, Anthony Liguori wrote:
>>>> On 07/23/2011 11:06 AM, Michael Roth wrote:
>>>>> On 07/23/2011 05:07 AM, Alexander Graf wrote:
>>>>>> 
>>>>>> On 20.07.2011, at 22:19, Michael Roth wrote:
>>>>>> 
>>>>>>> This is the actual guest daemon, it listens for requests over a
>>>>>>> virtio-serial/isa-serial/unix socket channel and routes them through
>>>>>>> to dispatch routines, and writes the results back to the channel in
>>>>>>> a manner similar to QMP.
>>>>>>> 
>>>>>>> A shorthand invocation:
>>>>>>> 
>>>>>>> qemu-ga -d
>>>>>>> 
>>>>>>> Is equivalent to:
>>>>>>> 
>>>>>>> qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \
>>>>>>> -f /var/run/qemu-ga.pid -d
>>>>>>> 
>>>>>>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>>>>>> 
>>>>>> A rebase on top of current HEAD gave me the following on openSUSE 11.1
>>>>>> PPC:
>>>>>> 
>>>>>> 
>>>>>> agraf@lychee:/home/agraf/release/qemu>  make
>>>>>> CC qemu-ga.o
>>>>>> qemu-ga.c:40: error: expected specifier-qualifier-list before ‘GSocket’
>>>> 
>>>> GIO is fairly new. It may not be available on openSUSE.
>>>> 
>>>> Mike, you probably need to do a configure test for GIO and if it's not
>>>> present, don't build qemu-ga.
>>> 
>>> It should've failed the glib probe in that case. I think we might need a compile test to catch this GSocket issue.
>>> 
>>> Rather than building qemu-ga when possible, should we just go ahead and add a configure option and only run the probes when it's set? At least until QMP/QEMU start formally using glib? If so, on or off by default?
>> 
>> In general, I like the workflow of adding a feature with default off and then enabling it after it has been in for a couple of weeks. Since this got pushed so late for 0.15, I'd personally prefer to see it as preview (disabled by default) in 0.15 and only enabled by default if the requirements are there on 0.16.
> 
> The only way something like this gets tested is to default it on.
> 
> We default off'd the I/O thread even after years we still don't have it enabled.
> 
> With respect to 0.15, this bit of code is totally isolated from everything else.  Worst case scenario, we just disable it on platforms where it doesn't work.  It presents no real risk to the stability of the release.

As you've seen, it can break builds. Why not wait for 0.16? The code came in more than 2 months after the soft feature freeze, which was specifically for big features like this, no?


Alex

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 16:10         ` Anthony Liguori
@ 2011-07-23 19:23           ` Jes Sorensen
  2011-07-23 19:38             ` Alexander Graf
  0 siblings, 1 reply; 30+ messages in thread
From: Jes Sorensen @ 2011-07-23 19:23 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Alexander Graf, agl, Michael Roth,
	qemu-devel@nongnu.org Developers, Luiz Capitulino, Blue Swirl,
	Anthony Liguori

On 07/23/11 18:10, Anthony Liguori wrote:
> 
>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
>>> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first
>>> use in this function)
>>> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is
>>> reported only once
>>> qga/guest-agent-commands.c:443: error: for each function it appears in.)
>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
>>> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use
>>> in this function)
> 
> The kernel probably doesn't implement FIFREEZE.  You need to do a
> configure test and set CONFIG_FSFREEZE appropriately.  I anticipated
> this and that's why I added CONFIG_FSFREEZE and didn't just do __linux__.

That would be odd, FIFREEZE has been around since at least January 2009
according to git blame (fcccf502540e3d752d33b2d8e976034dee81f9f7). Is
OpenSuSE 11 that old?

That said, having a test for it being present is a good idea.

Cheers,
Jes

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 19:23           ` Jes Sorensen
@ 2011-07-23 19:38             ` Alexander Graf
  0 siblings, 0 replies; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 19:38 UTC (permalink / raw)
  To: Jes Sorensen
  Cc: agl, qemu-devel@nongnu.org Developers, Michael Roth,
	Luiz Capitulino, Blue Swirl, Anthony Liguori

[-- Attachment #1: Type: text/plain, Size: 1134 bytes --]


On 23.07.2011, at 21:23, Jes Sorensen wrote:

> On 07/23/11 18:10, Anthony Liguori wrote:
>> 
>>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_freeze’:
>>>> qga/guest-agent-commands.c:443: error: ‘FIFREEZE’ undeclared (first
>>>> use in this function)
>>>> qga/guest-agent-commands.c:443: error: (Each undeclared identifier is
>>>> reported only once
>>>> qga/guest-agent-commands.c:443: error: for each function it appears in.)
>>>> qga/guest-agent-commands.c: In function ‘qmp_guest_fsfreeze_thaw’:
>>>> qga/guest-agent-commands.c:481: error: ‘FITHAW’ undeclared (first use
>>>> in this function)
>> 
>> The kernel probably doesn't implement FIFREEZE.  You need to do a
>> configure test and set CONFIG_FSFREEZE appropriately.  I anticipated
>> this and that's why I added CONFIG_FSFREEZE and didn't just do __linux__.
> 
> That would be odd, FIFREEZE has been around since at least January 2009
> according to git blame (fcccf502540e3d752d33b2d8e976034dee81f9f7). Is
> OpenSuSE 11 that old?

http://news.opensuse.org/2008/12/18/opensuse-111-released/

In short: yes, it is :)


Alex


[-- Attachment #2: Type: text/html, Size: 2692 bytes --]

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 19:22                 ` Alexander Graf
@ 2011-07-23 21:12                   ` Anthony Liguori
  2011-07-23 21:34                     ` Alexander Graf
  0 siblings, 1 reply; 30+ messages in thread
From: Anthony Liguori @ 2011-07-23 21:12 UTC (permalink / raw)
  To: Alexander Graf
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen, Michael Roth,
	Luiz Capitulino, Blue Swirl

On 07/23/2011 02:22 PM, Alexander Graf wrote:
>
>> We default off'd the I/O thread even after years we still don't have it enabled.
>>
>> With respect to 0.15, this bit of code is totally isolated from everything else.  Worst case scenario, we just disable it on platforms where it doesn't work.  It presents no real risk to the stability of the release.
>
> As you've seen, it can break builds. Why not wait for 0.16? The code came in more than 2 months after the soft feature freeze, which was specifically for big features like this, no?

I just sent out a patch that should fix the build issue.  Let's see what 
it takes to resolve this before we talk about disabling for 0.16.

Again, there's zero risk to QEMU for having this enabled so if we can 
resolve the build issues, and I don't see why we can't, then there 
should be no real problem here.

Regards,

Anthony Liguori

>
> Alex
>

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

* Re: [Qemu-devel] [PATCH] guest agent: qemu-ga daemon
  2011-07-23 21:12                   ` Anthony Liguori
@ 2011-07-23 21:34                     ` Alexander Graf
  0 siblings, 0 replies; 30+ messages in thread
From: Alexander Graf @ 2011-07-23 21:34 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: agl, qemu-devel@nongnu.org Developers, Jes Sorensen, Michael Roth,
	Luiz Capitulino, Blue Swirl


On 23.07.2011, at 23:12, Anthony Liguori wrote:

> On 07/23/2011 02:22 PM, Alexander Graf wrote:
>> 
>>> We default off'd the I/O thread even after years we still don't have it enabled.
>>> 
>>> With respect to 0.15, this bit of code is totally isolated from everything else.  Worst case scenario, we just disable it on platforms where it doesn't work.  It presents no real risk to the stability of the release.
>> 
>> As you've seen, it can break builds. Why not wait for 0.16? The code came in more than 2 months after the soft feature freeze, which was specifically for big features like this, no?
> 
> I just sent out a patch that should fix the build issue.  Let's see what it takes to resolve this before we talk about disabling for 0.16.

Oh, I'd immediately agree on saying it should be enabled for 0.16 :)

> Again, there's zero risk to QEMU for having this enabled so if we can resolve the build issues, and I don't see why we can't, then there should be no real problem here.

I've just tried out the two patches you sent. With them applied, things compile just fine.


Alex

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

end of thread, other threads:[~2011-07-23 21:34 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-19 20:41 [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v8 Michael Roth
2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 1/4] qerror: add QERR_JSON_PARSE_ERROR to qerror.c Michael Roth
2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 2/4] guest agent: command state class Michael Roth
2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 3/4] guest agent: qemu-ga daemon Michael Roth
2011-07-20 17:56   ` Luiz Capitulino
2011-07-20 19:25     ` Michael Roth
2011-07-20 19:31       ` Luiz Capitulino
2011-07-20 20:01         ` Michael Roth
2011-07-20 20:05           ` Michael Roth
2011-07-20 20:12             ` Luiz Capitulino
2011-07-20 20:00   ` [Qemu-devel] [PATCH] guest agent: qemu-ga, remove unused variables Michael Roth
2011-07-20 20:19   ` [Qemu-devel] [PATCH] guest agent: qemu-ga daemon Michael Roth
2011-07-23 10:07     ` Alexander Graf
2011-07-23 10:33       ` Alexander Graf
2011-07-23 16:06       ` Michael Roth
2011-07-23 16:10         ` Anthony Liguori
2011-07-23 19:23           ` Jes Sorensen
2011-07-23 19:38             ` Alexander Graf
2011-07-23 16:10         ` Anthony Liguori
2011-07-23 16:43           ` Michael Roth
2011-07-23 16:49             ` Anthony Liguori
2011-07-23 18:35               ` Alexander Graf
2011-07-23 19:12                 ` Anthony Liguori
2011-07-23 19:20                   ` Alexander Graf
2011-07-23 18:34             ` Alexander Graf
2011-07-23 19:14               ` Anthony Liguori
2011-07-23 19:22                 ` Alexander Graf
2011-07-23 21:12                   ` Anthony Liguori
2011-07-23 21:34                     ` Alexander Graf
2011-07-19 20:41 ` [Qemu-devel] [PATCH v8 4/4] guest agent: add guest agent RPCs/commands Michael Roth

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).