[Qemu-devel] [RFC 1/3] qemu-ga: add support for events

Tomáš Golembiovský posted 3 patches 8 years, 4 months ago
[Qemu-devel] [RFC 1/3] qemu-ga: add support for events
Posted by Tomáš Golembiovský 8 years, 4 months ago
Events can play an integral role when monitoring internal state of the
guest OS. This patch adds the core functionality for adding events to
QEMU Guest Agent.

Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
---
 Makefile               |  7 +++++-
 qga/Makefile.objs      |  2 +-
 qga/channel-posix.c    |  8 +++++++
 qga/channel-win32.c    |  6 +++++
 qga/channel.h          |  1 +
 qga/guest-agent-core.h |  1 +
 qga/main.c             | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qga/qapi-event.json    |  2 ++
 qga/qapi-schema.json   |  2 ++
 9 files changed, 86 insertions(+), 2 deletions(-)
 create mode 100644 qga/qapi-event.json

diff --git a/Makefile b/Makefile
index c830d7a46c..03e2174a18 100644
--- a/Makefile
+++ b/Makefile
@@ -408,6 +408,11 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
 		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
 		"GEN","$@")
+qga/qapi-generated/qga-qapi-event.c qga/qapi-generated/qga-qapi-event.h :\
+$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
+		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
+		"GEN","$@")
 
 qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
@@ -441,7 +446,7 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
 		$(gen-out-type) -o "." $<, \
 		"GEN","$@")
 
-QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
+QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h qga-qapi-event.h)
 $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
 
 qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS)
diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index 1c5986c0bb..24399b6325 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -3,6 +3,6 @@ qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
 qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
 qga-obj-$(CONFIG_WIN32) += vss-win32.o
 qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
-qga-obj-y += qapi-generated/qga-qmp-marshal.o
+qga-obj-y += qapi-generated/qga-qmp-marshal.o qapi-generated/qga-qapi-event.o
 
 qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/
diff --git a/qga/channel-posix.c b/qga/channel-posix.c
index 3f34465159..22e440724c 100644
--- a/qga/channel-posix.c
+++ b/qga/channel-posix.c
@@ -118,6 +118,14 @@ static int ga_channel_client_add(GAChannel *c, int fd)
     return 0;
 }
 
+gboolean ga_channel_client_attached(GAChannel *c)
+{
+    g_assert(c);
+    /* TODO: make this work with all methods. following works only with
+     * unix-listen */
+    return c->client_channel != NULL;
+}
+
 static gboolean ga_channel_open(GAChannel *c, const gchar *path,
                                 GAChannelMethod method, int fd)
 {
diff --git a/qga/channel-win32.c b/qga/channel-win32.c
index 7e6dc4d26f..b62a6a3859 100644
--- a/qga/channel-win32.c
+++ b/qga/channel-win32.c
@@ -315,6 +315,12 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
     return true;
 }
 
+gboolean ga_channel_client_attached(GAChannel *c)
+{
+    /* TODO: make this work with all methods */
+    return true;
+}
+
 GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
                           int listen_fd, GAChannelCallback cb, gpointer opaque)
 {
diff --git a/qga/channel.h b/qga/channel.h
index 1778416115..030ec9e551 100644
--- a/qga/channel.h
+++ b/qga/channel.h
@@ -30,5 +30,6 @@ GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
 void ga_channel_free(GAChannel *c);
 GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count);
 GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size);
+gboolean ga_channel_client_attached(GAChannel *c);
 
 #endif
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index 3e8a4acff2..47d6c73458 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -13,6 +13,7 @@
 #include "qapi/qmp/dispatch.h"
 #include "qemu-common.h"
 #include "qga-qmp-commands.h"
+#include "qga-qapi-event.h"
 
 #define QGA_READ_COUNT_DEFAULT 4096
 
diff --git a/qga/main.c b/qga/main.c
index cc58d2b53d..f16abb5cbb 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -23,6 +23,8 @@
 #include "qapi/qmp/qjson.h"
 #include "qga/guest-agent-core.h"
 #include "qemu/module.h"
+#include "qapi/qmp-event.h"
+#include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/dispatch.h"
 #include "qga/channel.h"
@@ -674,6 +676,59 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data)
     return true;
 }
 
+/* TODO: HACK HACK HACK... can't we get a GAstate somehow? */
+QDict *queued_event;
+static void ga_event_emit(qga_QAPIEvent event, QDict *qdict, Error **errp)
+{
+    if (queued_event) {
+        error_setg(errp, "unsent event already waiting");
+    } else {
+        QINCREF(qdict);
+        queued_event = qdict;
+    }
+}
+/* HACK HACK HACK!!! */
+
+static gboolean monitoring_cb(gpointer data)
+{
+    Error *err = NULL;
+    GAState *s = (GAState *)data;
+
+    g_assert(s->channel);
+    g_warning("monitoring!");
+
+    if (!ga_channel_client_attached(s->channel)) {
+        goto ok;
+    }
+
+    /* TODO: call something */
+    goto ok;
+
+/*fail:*/
+    g_assert(err);
+    g_warning("%s", error_get_pretty(err));
+    error_free(err);
+
+ok:
+    /* Always return true. False would remove this callback. */
+    return true;
+}
+
+static gboolean monitoring_init(GAState *s)
+{
+    if (g_timeout_add_seconds(5, monitoring_cb, (gpointer)s) <= 0) {
+        g_error("failed to create monitoring timer");
+        goto fail;
+    }
+    g_debug("monitoring timer created");
+
+    qmp_event_set_func_emit(ga_event_emit);
+    return true;
+
+fail:
+    return false;
+}
+
 static gboolean channel_init(GAState *s, const gchar *method, const gchar *path,
                              int listen_fd)
 {
@@ -1330,6 +1385,10 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
         g_critical("failed to initialize guest agent channel");
         return EXIT_FAILURE;
     }
+
+    /* TODO: error handling? */
+    monitoring_init(ga_state);
+
 #ifndef _WIN32
     g_main_loop_run(ga_state->main_loop);
 #else
diff --git a/qga/qapi-event.json b/qga/qapi-event.json
new file mode 100644
index 0000000000..9c14e4609e
--- /dev/null
+++ b/qga/qapi-event.json
@@ -0,0 +1,2 @@
+# *-*- Mode: Python -*-*
+
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 03743ab905..f30ba183bb 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1,5 +1,7 @@
 # *-*- Mode: Python -*-*
 
+{ 'include': 'qapi-event.json' }
+
 ##
 #
 # General note concerning the use of guest agent interfaces:
-- 
2.13.1


Re: [Qemu-devel] [RFC 1/3] qemu-ga: add support for events
Posted by Eric Blake 8 years, 4 months ago
On 06/23/2017 08:02 AM, Tomáš Golembiovský wrote:
> Events can play an integral role when monitoring internal state of the
> guest OS. This patch adds the core functionality for adding events to
> QEMU Guest Agent.

Will sending events confuse existing clients that aren't expecting them?
Do we need to add some sort of protocol handshake when the client first
connects so that we know whether the client is able to parse events
(including ignoring unknown events)?  (It's a shame that events were
built into QMP from the beginning, so we've never had to figure out how
to portably hand-shake a client/server negotiation on additional
features to be used across the wire)

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

Re: [Qemu-devel] [RFC 1/3] qemu-ga: add support for events
Posted by Tomáš Golembiovský 8 years, 3 months ago
On Fri, 7 Jul 2017 15:53:37 -0500
Eric Blake <eblake@redhat.com> wrote:

> On 06/23/2017 08:02 AM, Tomáš Golembiovský wrote:
> > Events can play an integral role when monitoring internal state of the
> > guest OS. This patch adds the core functionality for adding events to
> > QEMU Guest Agent.  
> 
> Will sending events confuse existing clients that aren't expecting them?
> Do we need to add some sort of protocol handshake when the client first
> connects so that we know whether the client is able to parse events
> (including ignoring unknown events)?  (It's a shame that events were
> built into QMP from the beginning, so we've never had to figure out how
> to portably hand-shake a client/server negotiation on additional
> features to be used across the wire)

You're probably right. Something like this will be necessary.

> 
> -- 
> Eric Blake, Principal Software Engineer
> Red Hat, Inc.           +1-919-301-3266
> Virtualization:  qemu.org | libvirt.org
> 


-- 
Tomáš Golembiovský <tgolembi@redhat.com>