These are the first async events in the qemu protocol, so this
patch looks rather big compared to most RPC additions. However,
a large majority of this patch is just mechanical copy-and-paste
from recently-added network events. It didn't help that this
is also the first virConnect rather than virDomain prefix
associated with a qemu-specific API.
* src/remote/qemu_protocol.x (qemu_*_domain_monitor_event_*): New
structs and RPC messages.
* src/rpc/gendispatch.pl: Adjust naming conventions.
* daemon/libvirtd.h (daemonClientPrivate): Track qemu events.
* daemon/remote.c (remoteClientFreeFunc): Likewise.
(remoteRelayDomainQemuMonitorEvent)
(qemuDispatchConnectDomainMonitorEventRegister)
(qemuDispatchConnectDomainMonitorEventDeregister): New functions.
* src/remote/remote_driver.c (qemuEvents): Handle qemu events.
(doRemoteOpen): Register for events.
(remoteNetworkBuildEventLifecycle)
(remoteConnectDomainQemuMonitorEventRegister)
(remoteConnectDomainQemuMonitorEventDeregister): New functions.
* src/qemu_protocol-structs: Regenerate.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
daemon/libvirtd.h | 2 +
daemon/remote.c | 209 +++++++++++++++++++++++++++++++++++++++++++++
src/qemu_protocol-structs | 22 +++++
src/remote/qemu_protocol.x | 50 ++++++++++-
src/remote/remote_driver.c | 143 ++++++++++++++++++++++++++++++-
src/rpc/gendispatch.pl | 13 +--
6 files changed, 428 insertions(+), 11 deletions(-)
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
index c4f1f27..49df33e 100644
--- a/daemon/libvirtd.h
+++ b/daemon/libvirtd.h
@@ -55,6 +55,8 @@ struct daemonClientPrivate {
size_t ndomainEventCallbacks;
daemonClientEventCallbackPtr *networkEventCallbacks;
size_t nnetworkEventCallbacks;
+ daemonClientEventCallbackPtr *qemuEventCallbacks;
+ size_t nqemuEventCallbacks;
# if WITH_SASL
virNetSASLSessionPtr sasl;
diff --git a/daemon/remote.c b/daemon/remote.c
index b48d456..f18ce6b 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -53,6 +53,7 @@
#include "domain_conf.h"
#include "network_conf.h"
#include "viraccessapicheck.h"
+#include "viraccessapicheckqemu.h"
#define VIR_FROM_THIS VIR_FROM_RPC
@@ -186,6 +187,33 @@ cleanup:
}
+static bool
+remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client,
+ virConnectPtr conn, virDomainPtr dom)
+{
+ virDomainDef def;
+ virIdentityPtr identity = NULL;
+ bool ret = false;
+
+ /* For now, we just create a virDomainDef with enough contents to
+ * satisfy what viraccessdriverpolkit.c references. This is a bit
+ * fragile, but I don't know of anything better. */
+ def.name = dom->name;
+ memcpy(def.uuid, dom->uuid, VIR_UUID_BUFLEN);
+
+ if (!(identity = virNetServerClientGetIdentity(client)))
+ goto cleanup;
+ if (virIdentitySetCurrent(identity) < 0)
+ goto cleanup;
+ ret = virConnectDomainQemuMonitorEventRegisterCheckACL(conn, &def);
+
+cleanup:
+ ignore_value(virIdentitySetCurrent(NULL));
+ virObjectUnref(identity);
+ return ret;
+}
+
+
static int
remoteRelayDomainEventLifecycle(virConnectPtr conn,
virDomainPtr dom,
@@ -958,6 +986,52 @@ static virConnectNetworkEventGenericCallback networkEventCallbacks[]
= {
verify(ARRAY_CARDINALITY(networkEventCallbacks) == VIR_NETWORK_EVENT_ID_LAST);
+static void
+remoteRelayDomainQemuMonitorEvent(virConnectPtr conn,
+ virDomainPtr dom,
+ const char *event,
+ long long seconds,
+ unsigned int micros,
+ const char *details,
+ void *opaque)
+{
+ daemonClientEventCallbackPtr callback = opaque;
+ qemu_domain_monitor_event_msg data;
+ char **details_p = NULL;
+
+ if (callback->callbackID < 0 ||
+ !remoteRelayDomainQemuMonitorEventCheckACL(callback->client, conn,
+ dom))
+ return;
+
+ VIR_DEBUG("Relaying qemu monitor event %s %s, callback %d",
+ event, details, callback->callbackID);
+
+ /* build return data */
+ memset(&data, 0, sizeof(data));
+ data.callbackID = callback->callbackID;
+ if (VIR_STRDUP(data.event, event) < 0)
+ goto error;
+ data.seconds = seconds;
+ data.micros = micros;
+ if (details &&
+ ((VIR_ALLOC(details_p) < 0) ||
+ VIR_STRDUP(*details_p, details) < 0))
+ goto error;
+ data.details = details_p;
+ make_nonnull_domain(&data.dom, dom);
+
+ remoteDispatchObjectEventSend(callback->client, qemuProgram,
+ QEMU_PROC_DOMAIN_MONITOR_EVENT,
+ (xdrproc_t)xdr_qemu_domain_monitor_event_msg,
+ &data);
+ return;
+
+error:
+ VIR_FREE(data.event);
+ VIR_FREE(details_p);
+}
+
/*
* You must hold lock for at least the client
* We don't free stuff here, merely disconnect the client's
@@ -1005,6 +1079,21 @@ void remoteClientFreeFunc(void *data)
}
VIR_FREE(priv->networkEventCallbacks);
+ for (i = 0; i < priv->nqemuEventCallbacks; i++) {
+ int callbackID = priv->qemuEventCallbacks[i]->callbackID;
+ if (callbackID < 0) {
+ VIR_WARN("unexpected incomplete qemu monitor callback %zu",
i);
+ continue;
+ }
+ VIR_DEBUG("Deregistering remote qemu monitor event relay %d",
+ callbackID);
+ priv->qemuEventCallbacks[i]->callbackID = -1;
+ if (virConnectDomainQemuMonitorEventDeregister(priv->conn,
+ callbackID) < 0)
+ VIR_WARN("unexpected qemu monitor event deregister failure");
+ }
+ VIR_FREE(priv->qemuEventCallbacks);
+
virConnectClose(priv->conn);
virIdentitySetCurrent(NULL);
@@ -5861,6 +5950,126 @@ cleanup:
}
+static int
+qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr
ATTRIBUTE_UNUSED,
+
qemu_connect_domain_monitor_event_register_args *args,
+
qemu_connect_domain_monitor_event_register_ret *ret)
+{
+ int callbackID;
+ int rv = -1;
+ daemonClientEventCallbackPtr callback = NULL;
+ daemonClientEventCallbackPtr ref;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+ virDomainPtr dom = NULL;
+ const char *event = args->event ? *args->event : NULL;
+
+ if (!priv->conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not
open"));
+ goto cleanup;
+ }
+
+ virMutexLock(&priv->lock);
+
+ if (args->dom &&
+ !(dom = get_nonnull_domain(priv->conn, *args->dom)))
+ goto cleanup;
+
+ /* If we call register first, we could append a complete callback
+ * to our array, but on OOM append failure, we'd have to then hope
+ * deregister works to undo our register. So instead we append an
+ * incomplete callback to our array, then register, then fix up
+ * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+ * success, we use 'ref' to save a copy of the pointer. */
+ if (VIR_ALLOC(callback) < 0)
+ goto cleanup;
+ callback->client = client;
+ callback->callbackID = -1;
+ ref = callback;
+ if (VIR_APPEND_ELEMENT(priv->qemuEventCallbacks,
+ priv->nqemuEventCallbacks,
+ callback) < 0)
+ goto cleanup;
+
+ if ((callbackID = virConnectDomainQemuMonitorEventRegister(priv->conn,
+ dom,
+ event,
+
remoteRelayDomainQemuMonitorEvent,
+ ref,
+ remoteEventCallbackFree,
+ args->flags)) < 0)
{
+ VIR_SHRINK_N(priv->qemuEventCallbacks,
+ priv->nqemuEventCallbacks, 1);
+ callback = ref;
+ goto cleanup;
+ }
+
+ ref->callbackID = callbackID;
+ ret->callbackID = callbackID;
+
+ rv = 0;
+
+cleanup:
+ VIR_FREE(callback);
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ if (dom)
+ virDomainFree(dom);
+ virMutexUnlock(&priv->lock);
+ return rv;
+}
+
+
+static int
+qemuDispatchConnectDomainMonitorEventDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr
ATTRIBUTE_UNUSED,
+
qemu_connect_domain_monitor_event_deregister_args *args)
+{
+ int rv = -1;
+ size_t i;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+
+ if (!priv->conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not
open"));
+ goto cleanup;
+ }
+
+ virMutexLock(&priv->lock);
+
+ for (i = 0; i < priv->nqemuEventCallbacks; i++) {
+ if (priv->qemuEventCallbacks[i]->callbackID == args->callbackID)
+ break;
+ }
+ if (i == priv->nqemuEventCallbacks) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("qemu monitor event callback %d not registered"),
+ args->callbackID);
+ goto cleanup;
+ }
+
+ if (virConnectDomainQemuMonitorEventDeregister(priv->conn,
+ args->callbackID) < 0)
+ goto cleanup;
+
+ VIR_DELETE_ELEMENT(priv->qemuEventCallbacks, i,
+ priv->nqemuEventCallbacks);
+
+ rv = 0;
+
+cleanup:
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ virMutexUnlock(&priv->lock);
+ return rv;
+}
+
+
/*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire
diff --git a/src/qemu_protocol-structs b/src/qemu_protocol-structs
index 0dcd2c6..8501543 100644
--- a/src/qemu_protocol-structs
+++ b/src/qemu_protocol-structs
@@ -28,8 +28,30 @@ struct qemu_domain_agent_command_args {
struct qemu_domain_agent_command_ret {
remote_string result;
};
+struct qemu_connect_domain_monitor_event_register_args {
+ remote_domain dom;
+ remote_string event;
+ u_int flags;
+};
+struct qemu_connect_domain_monitor_event_register_ret {
+ int callbackID;
+};
+struct qemu_connect_domain_monitor_event_deregister_args {
+ int callbackID;
+};
+struct qemu_domain_monitor_event_msg {
+ int callbackID;
+ remote_nonnull_domain dom;
+ remote_nonnull_string event;
+ int64_t seconds;
+ u_int micros;
+ remote_string details;
+};
enum qemu_procedure {
QEMU_PROC_DOMAIN_MONITOR_COMMAND = 1,
QEMU_PROC_DOMAIN_ATTACH = 2,
QEMU_PROC_DOMAIN_AGENT_COMMAND = 3,
+ QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER = 4,
+ QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER = 5,
+ QEMU_PROC_DOMAIN_MONITOR_EVENT = 6,
};
diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x
index 1e7cf7c..f6b88a9 100644
--- a/src/remote/qemu_protocol.x
+++ b/src/remote/qemu_protocol.x
@@ -3,7 +3,7 @@
* remote_internal driver and libvirtd. This protocol is
* internal and may change at any time.
*
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -58,6 +58,30 @@ struct qemu_domain_agent_command_ret {
remote_string result;
};
+
+struct qemu_connect_domain_monitor_event_register_args {
+ remote_domain dom;
+ remote_string event;
+ unsigned int flags;
+};
+
+struct qemu_connect_domain_monitor_event_register_ret {
+ int callbackID;
+};
+
+struct qemu_connect_domain_monitor_event_deregister_args {
+ int callbackID;
+};
+
+struct qemu_domain_monitor_event_msg {
+ int callbackID;
+ remote_nonnull_domain dom;
+ remote_nonnull_string event;
+ hyper seconds;
+ unsigned int micros;
+ remote_string details;
+};
+
/* Define the program number, protocol version and procedure numbers here. */
const QEMU_PROGRAM = 0x20008087;
const QEMU_PROTOCOL_VERSION = 1;
@@ -108,5 +132,27 @@ enum qemu_procedure {
* @priority: low
* @acl: domain:write
*/
- QEMU_PROC_DOMAIN_AGENT_COMMAND = 3
+ QEMU_PROC_DOMAIN_AGENT_COMMAND = 3,
+
+ /**
+ * @generate: none
+ * @priority: high
+ * @acl: connect:search_domains
+ * @acl: connect:write
+ * @aclfilter: domain:getattr
+ */
+ QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER = 4,
+
+ /**
+ * @generate: none
+ * @priority: high
+ * @acl: connect:write
+ */
+ QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER = 5,
+
+ /**
+ * @generate: both
+ * @acl: none
+ */
+ QEMU_PROC_DOMAIN_MONITOR_EVENT = 6
};
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 955465a..50745fa 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -498,6 +498,19 @@ static virNetClientProgramEvent remoteEvents[] = {
(xdrproc_t)xdr_remote_domain_event_callback_device_removed_msg },
};
+
+static void
+remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+ virNetClientPtr client ATTRIBUTE_UNUSED,
+ void *evdata, void *opaque);
+
+static virNetClientProgramEvent qemuEvents[] = {
+ { QEMU_PROC_DOMAIN_MONITOR_EVENT,
+ remoteDomainBuildQemuMonitorEvent,
+ sizeof(qemu_domain_monitor_event_msg),
+ (xdrproc_t)xdr_qemu_domain_monitor_event_msg },
+};
+
enum virDrvOpenRemoteFlags {
VIR_DRV_OPEN_REMOTE_RO = (1 << 0),
VIR_DRV_OPEN_REMOTE_USER = (1 << 1), /* Use the per-user socket path */
@@ -975,9 +988,9 @@ doRemoteOpen(virConnectPtr conn,
goto failed;
if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM,
QEMU_PROTOCOL_VERSION,
- NULL,
- 0,
- NULL)))
+ qemuEvents,
+ ARRAY_CARDINALITY(qemuEvents),
+ conn)))
goto failed;
if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 ||
@@ -3174,6 +3187,103 @@ done:
static int
+remoteConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
+ virDomainPtr dom,
+ const char *event,
+ virConnectDomainQemuMonitorEventCallback
callback,
+ void *opaque,
+ virFreeCallback freecb,
+ unsigned int flags)
+{
+ int rv = -1;
+ struct private_data *priv = conn->privateData;
+ qemu_connect_domain_monitor_event_register_args args;
+ qemu_connect_domain_monitor_event_register_ret ret;
+ int callbackID;
+ int count;
+ remote_nonnull_domain domain;
+
+ remoteDriverLock(priv);
+
+ if ((count = virDomainQemuMonitorEventStateRegisterID(conn,
+ priv->eventState,
+ dom, event, callback,
+ opaque, freecb, -1,
+ &callbackID)) < 0)
+ goto done;
+
+ /* If this is the first callback for this event, we need to enable
+ * events on the server */
+ if (count == 1) {
+ if (dom) {
+ make_nonnull_domain(&domain, dom);
+ args.dom = &domain;
+ } else {
+ args.dom = NULL;
+ }
+ args.event = event ? (char **) &event : NULL;
+ args.flags = flags;
+
+ memset(&ret, 0, sizeof(ret));
+ if (call(conn, priv, REMOTE_CALL_QEMU,
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER,
+ (xdrproc_t) xdr_qemu_connect_domain_monitor_event_register_args, (char
*) &args,
+ (xdrproc_t) xdr_qemu_connect_domain_monitor_event_register_ret, (char *)
&ret) == -1) {
+ virObjectEventStateDeregisterID(conn, priv->eventState,
+ callbackID);
+ goto done;
+ }
+ virObjectEventStateSetRemote(conn, priv->eventState, callbackID,
+ ret.callbackID);
+ }
+
+ rv = callbackID;
+
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+
+static int
+remoteConnectDomainQemuMonitorEventDeregister(virConnectPtr conn,
+ int callbackID)
+{
+ struct private_data *priv = conn->privateData;
+ int rv = -1;
+ qemu_connect_domain_monitor_event_deregister_args args;
+ int remoteID;
+ int count;
+
+ remoteDriverLock(priv);
+
+ if (virObjectEventStateEventID(conn, priv->eventState,
+ callbackID, &remoteID) < 0)
+ goto done;
+
+ if ((count = virObjectEventStateDeregisterID(conn, priv->eventState,
+ callbackID)) < 0)
+ goto done;
+
+ /* If that was the last callback for this event, we need to disable
+ * events on the server */
+ if (count == 0) {
+ args.callbackID = remoteID;
+
+ if (call(conn, priv, REMOTE_CALL_QEMU,
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER,
+ (xdrproc_t) xdr_qemu_connect_domain_monitor_event_deregister_args, (char
*) &args,
+ (xdrproc_t) xdr_void, (char *) NULL) == -1)
+ goto done;
+ }
+
+ rv = 0;
+
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+
+static int
remoteConnectListAllInterfaces(virConnectPtr conn,
virInterfacePtr **ifaces,
unsigned int flags)
@@ -5408,6 +5518,31 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog
ATTRIBUTE_UNUSED,
}
+static void
+remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+ virNetClientPtr client ATTRIBUTE_UNUSED,
+ void *evdata, void *opaque)
+{
+ virConnectPtr conn = opaque;
+ struct private_data *priv = conn->privateData;
+ qemu_domain_monitor_event_msg *msg = evdata;
+ virDomainPtr dom;
+ virObjectEventPtr event = NULL;
+
+ dom = get_nonnull_domain(conn, msg->dom);
+ if (!dom)
+ return;
+
+ event = virDomainQemuMonitorEventNew(dom->id, dom->name, dom->uuid,
+ msg->event, msg->seconds,
+ msg->micros,
+ msg->details ? *msg->details : NULL);
+ virDomainFree(dom);
+
+ remoteEventQueue(priv, event, msg->callbackID);
+}
+
+
static virDrvOpenStatus ATTRIBUTE_NONNULL(1)
remoteSecretOpen(virConnectPtr conn, virConnectAuthPtr auth,
unsigned int flags)
@@ -7618,6 +7753,8 @@ static virDriver remote_driver = {
.domainQemuMonitorCommand = remoteDomainQemuMonitorCommand, /* 0.8.3 */
.domainQemuAttach = remoteDomainQemuAttach, /* 0.9.4 */
.domainQemuAgentCommand = remoteDomainQemuAgentCommand, /* 0.10.0 */
+ .connectDomainQemuMonitorEventRegister = remoteConnectDomainQemuMonitorEventRegister,
/* 1.2.3 */
+ .connectDomainQemuMonitorEventDeregister =
remoteConnectDomainQemuMonitorEventDeregister, /* 1.2.3 */
.domainOpenConsole = remoteDomainOpenConsole, /* 0.8.6 */
.domainOpenChannel = remoteDomainOpenChannel, /* 1.0.2 */
.domainOpenGraphics = remoteDomainOpenGraphics, /* 0.9.7 */
diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl
index ceb1ad8..b76bbac 100755
--- a/src/rpc/gendispatch.pl
+++ b/src/rpc/gendispatch.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
-# Copyright (C) 2010-2013 Red Hat, Inc.
+# Copyright (C) 2010-2014 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -927,8 +927,9 @@ elsif ($mode eq "server") {
push(@args_list, "priv->conn");
}
- if ($structprefix eq "qemu" && $call->{ProcName} =~
/^Domain/) {
- $proc_name =~ s/^(Domain)/${1}Qemu/;
+ if ($structprefix eq "qemu" &&
+ $call->{ProcName} =~ /^(Connect)?Domain/) {
+ $proc_name =~ s/^((Connect)?Domain)/${1}Qemu/;
}
if ($structprefix eq "lxc" && $call->{ProcName} =~
/^Domain/) {
$proc_name =~ s/^(Domain)/${1}Lxc/;
@@ -1704,7 +1705,7 @@ elsif ($mode eq "client") {
if ($mode eq "aclsym") {
my $apiname = "vir" . $call->{ProcName};
if ($structprefix eq "qemu") {
- $apiname =~ s/virDomain/virDomainQemu/;
+ $apiname =~ s/(vir(Connect)?Domain)/${1}Qemu/;
} elsif ($structprefix eq "lxc") {
$apiname =~ s/virDomain/virDomainLxc/;
}
@@ -1744,7 +1745,7 @@ elsif ($mode eq "client") {
my $apiname = "vir" . $call->{ProcName};
if ($structprefix eq "qemu") {
- $apiname =~ s/virDomain/virDomainQemu/;
+ $apiname =~ s/(vir(Connect)?Domain)/${1}Qemu/;
} elsif ($structprefix eq "lxc") {
$apiname =~ s/virDomain/virDomainLxc/;
}
@@ -1856,7 +1857,7 @@ elsif ($mode eq "client") {
my $apiname = "vir" . $call->{ProcName};
if ($structprefix eq "qemu") {
- $apiname =~ s/virDomain/virDomainQemu/;
+ $apiname =~ s/(vir(Connect)?Domain)/${1}Qemu/;
} elsif ($structprefix eq "lxc") {
$apiname =~ s/virDomain/virDomainLxc/;
}
--
1.8.5.3