[libvirt] [PATCH] Fix tracking of RPC messages wrt streams
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
Commit 2c85644b0b51fbe5b6244e6773531af29933a727 attempted to
fix a problem with tracking RPC messages from streams by doing
- if (msg->header.type == VIR_NET_REPLY) {
+ if (msg->header.type == VIR_NET_REPLY ||
+ (msg->header.type == VIR_NET_STREAM &&
+ msg->header.status != VIR_NET_CONTINUE)) {
client->nrequests--;
In other words any stream packet, with status NET_OK or NET_ERROR
would cause nrequests to be decremented. This is great if the
packet from from a synchronous virStreamFinish or virStreamAbort
API call, but wildly wrong if from a server initiated abort.
The latter resulted in 'nrequests' being decremented below zero.
This then causes all I/O for that client to be stopped.
Instead of trying to infer whether we need to decrement the
nrequests field, from the message type/status, introduce an
explicit 'bool tracked' field to mark whether the virNetMessagePtr
object is subject to tracking.
Also add a virNetMessageClear function to allow a message
contents to be cleared out, without adversely impacting the
'tracked' field as a naive memset() would do
* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h: Add
a 'bool tracked' field and virNetMessageClear() API
* daemon/remote.c, daemon/stream.c, src/rpc/virnetclientprogram.c,
src/rpc/virnetclientstream.c, src/rpc/virnetserverclient.c,
src/rpc/virnetserverprogram.c: Switch over to use
virNetMessageClear() and pass in the 'bool tracked' value
when creating messages.
---
daemon/remote.c | 2 +-
daemon/stream.c | 10 +++++-----
src/rpc/virnetclientprogram.c | 2 +-
src/rpc/virnetclientstream.c | 4 ++--
src/rpc/virnetmessage.c | 14 ++++++++++++--
src/rpc/virnetmessage.h | 6 +++++-
src/rpc/virnetserverclient.c | 12 +++++-------
src/rpc/virnetserverprogram.c | 2 +-
8 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 34c6364..d5ead81 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -2495,7 +2495,7 @@ remoteDispatchDomainEventSend(virNetServerClientPtr client,
{
virNetMessagePtr msg;
- if (!(msg = virNetMessageNew()))
+ if (!(msg = virNetMessageNew(false)))
goto cleanup;
msg->header.prog = virNetServerProgramGetID(program);
diff --git a/daemon/stream.c b/daemon/stream.c
index ba3adc2..e3214c2 100644
--- a/daemon/stream.c
+++ b/daemon/stream.c
@@ -207,7 +207,7 @@ daemonStreamEvent(virStreamPtr st, int events, void *opaque)
virNetError(VIR_ERR_RPC,
"%s", _("stream had I/O failure"));
- msg = virNetMessageNew();
+ msg = virNetMessageNew(false);
if (!msg) {
ret = -1;
} else {
@@ -344,7 +344,7 @@ int daemonFreeClientStream(virNetServerClientPtr client,
virNetMessagePtr tmp = msg->next;
if (client) {
/* Send a dummy reply to free up 'msg' & unblock client rx */
- memset(msg, 0, sizeof(*msg));
+ virNetMessageClear(msg);
msg->header.type = VIR_NET_REPLY;
if (virNetServerClientSendMessage(client, msg) < 0) {
virNetServerClientImmediateClose(client);
@@ -653,7 +653,7 @@ daemonStreamHandleWrite(virNetServerClientPtr client,
* its active request count / throttling
*/
if (msg->header.status == VIR_NET_CONTINUE) {
- memset(msg, 0, sizeof(*msg));
+ virNetMessageClear(msg);
msg->header.type = VIR_NET_REPLY;
if (virNetServerClientSendMessage(client, msg) < 0) {
virNetMessageFree(msg);
@@ -715,7 +715,7 @@ daemonStreamHandleRead(virNetServerClientPtr client,
memset(&rerr, 0, sizeof(rerr));
- if (!(msg = virNetMessageNew()))
+ if (!(msg = virNetMessageNew(false)))
ret = -1;
else
ret = virNetServerProgramSendStreamError(remoteProgram,
@@ -729,7 +729,7 @@ daemonStreamHandleRead(virNetServerClientPtr client,
stream->tx = 0;
if (ret == 0)
stream->recvEOF = 1;
- if (!(msg = virNetMessageNew()))
+ if (!(msg = virNetMessageNew(false)))
ret = -1;
if (msg) {
diff --git a/src/rpc/virnetclientprogram.c b/src/rpc/virnetclientprogram.c
index c39520a..a07b744 100644
--- a/src/rpc/virnetclientprogram.c
+++ b/src/rpc/virnetclientprogram.c
@@ -272,7 +272,7 @@ int virNetClientProgramCall(virNetClientProgramPtr prog,
{
virNetMessagePtr msg;
- if (!(msg = virNetMessageNew()))
+ if (!(msg = virNetMessageNew(false)))
return -1;
msg->header.prog = prog->program;
diff --git a/src/rpc/virnetclientstream.c b/src/rpc/virnetclientstream.c
index fe15acd..2cc84d4 100644
--- a/src/rpc/virnetclientstream.c
+++ b/src/rpc/virnetclientstream.c
@@ -328,7 +328,7 @@ int virNetClientStreamSendPacket(virNetClientStreamPtr st,
bool wantReply;
VIR_DEBUG("st=%p status=%d data=%p nbytes=%zu", st, status, data, nbytes);
- if (!(msg = virNetMessageNew()))
+ if (!(msg = virNetMessageNew(false)))
return -1;
virMutexLock(&st->lock);
@@ -390,7 +390,7 @@ int virNetClientStreamRecvPacket(virNetClientStreamPtr st,
goto cleanup;
}
- if (!(msg = virNetMessageNew())) {
+ if (!(msg = virNetMessageNew(false))) {
virReportOOMError();
goto cleanup;
}
diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c
index 0725491..a1ae9c4 100644
--- a/src/rpc/virnetmessage.c
+++ b/src/rpc/virnetmessage.c
@@ -32,7 +32,7 @@
virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \
__FUNCTION__, __LINE__, __VA_ARGS__)
-virNetMessagePtr virNetMessageNew(void)
+virNetMessagePtr virNetMessageNew(bool tracked)
{
virNetMessagePtr msg;
@@ -41,11 +41,21 @@ virNetMessagePtr virNetMessageNew(void)
return NULL;
}
- VIR_DEBUG("msg=%p", msg);
+ msg->tracked = tracked;
+ VIR_DEBUG("msg=%p tracked=%d", msg, tracked);
return msg;
}
+
+void virNetMessageClear(virNetMessagePtr msg)
+{
+ bool tracked = msg->tracked;
+ memset(msg, 0, sizeof(*msg));
+ msg->tracked = tracked;
+}
+
+
void virNetMessageFree(virNetMessagePtr msg)
{
if (!msg)
diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h
index 2aae3f6..307a041 100644
--- a/src/rpc/virnetmessage.h
+++ b/src/rpc/virnetmessage.h
@@ -35,6 +35,8 @@ typedef void (*virNetMessageFreeCallback)(virNetMessagePtr msg, void *opaque);
* use virNetMessageNew() to allocate on the heap
*/
struct _virNetMessage {
+ bool tracked;
+
char buffer[VIR_NET_MESSAGE_MAX + VIR_NET_MESSAGE_LEN_MAX];
size_t bufferLength;
size_t bufferOffset;
@@ -48,7 +50,9 @@ struct _virNetMessage {
};
-virNetMessagePtr virNetMessageNew(void);
+virNetMessagePtr virNetMessageNew(bool tracked);
+
+void virNetMessageClear(virNetMessagePtr);
void virNetMessageFree(virNetMessagePtr msg);
diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c
index a73b06d..412814d 100644
--- a/src/rpc/virnetserverclient.c
+++ b/src/rpc/virnetserverclient.c
@@ -277,7 +277,7 @@ virNetServerClientCheckAccess(virNetServerClientPtr client)
return -1;
}
- if (!(confirm = virNetMessageNew()))
+ if (!(confirm = virNetMessageNew(false)))
return -1;
/* Checks have succeeded. Write a '\1' byte back to the client to
@@ -323,7 +323,7 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock,
virNetTLSContextRef(tls);
/* Prepare one for packet receive */
- if (!(client->rx = virNetMessageNew()))
+ if (!(client->rx = virNetMessageNew(true)))
goto error;
client->rx->bufferLength = VIR_NET_MESSAGE_LEN_MAX;
client->nrequests = 1;
@@ -805,7 +805,7 @@ readmore:
/* Possibly need to create another receive buffer */
if (client->nrequests < client->nrequests_max) {
- if (!(client->rx = virNetMessageNew())) {
+ if (!(client->rx = virNetMessageNew(true))) {
client->wantClose = true;
} else {
client->rx->bufferLength = VIR_NET_MESSAGE_LEN_MAX;
@@ -885,16 +885,14 @@ virNetServerClientDispatchWrite(virNetServerClientPtr client)
/* Get finished msg from head of tx queue */
msg = virNetMessageQueueServe(&client->tx);
- if (msg->header.type == VIR_NET_REPLY ||
- (msg->header.type == VIR_NET_STREAM &&
- msg->header.status != VIR_NET_CONTINUE)) {
+ if (msg->tracked) {
client->nrequests--;
/* See if the recv queue is currently throttled */
if (!client->rx &&
client->nrequests < client->nrequests_max) {
/* Ready to recv more messages */
+ virNetMessageClear(msg);
client->rx = msg;
- memset(client->rx, 0, sizeof(*client->rx));
client->rx->bufferLength = VIR_NET_MESSAGE_LEN_MAX;
msg = NULL;
client->nrequests++;
diff --git a/src/rpc/virnetserverprogram.c b/src/rpc/virnetserverprogram.c
index 2e9e3f7..643a97d 100644
--- a/src/rpc/virnetserverprogram.c
+++ b/src/rpc/virnetserverprogram.c
@@ -284,7 +284,7 @@ int virNetServerProgramDispatch(virNetServerProgramPtr prog,
VIR_INFO("Ignoring unexpected stream data serial=%d proc=%d status=%d",
msg->header.serial, msg->header.proc, msg->header.status);
/* Send a dummy reply to free up 'msg' & unblock client rx */
- memset(msg, 0, sizeof(*msg));
+ virNetMessageClear(msg);
msg->header.type = VIR_NET_REPLY;
if (virNetServerClientSendMessage(client, msg) < 0) {
ret = -1;
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] Avoid use-after-free on streams, due to message callbacks
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
When sending outbound stream RPC messages, a callback is
used to re-enable stream data transmission. If the stream
aborts while one of these messages is outstanding, the
stream may have been free'd by the time it is invoked. This
results in a use-after-free error
* daemon/stream.c: Ref-count streams to avoid use-after-free
---
daemon/stream.c | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/daemon/stream.c b/daemon/stream.c
index 7d2b367..ba3adc2 100644
--- a/daemon/stream.c
+++ b/daemon/stream.c
@@ -38,6 +38,7 @@
struct daemonClientStream {
daemonClientPrivatePtr priv;
+ int refs;
virNetServerProgramPtr prog;
@@ -102,6 +103,8 @@ daemonStreamMessageFinished(virNetMessagePtr msg,
stream->tx = 1;
daemonStreamUpdateEvents(stream);
+
+ daemonFreeClientStream(NULL, stream);
}
@@ -299,6 +302,7 @@ daemonCreateClientStream(virNetServerClientPtr client,
return NULL;
}
+ stream->refs = 1;
stream->priv = priv;
stream->prog = prog;
stream->procedure = header->proc;
@@ -326,6 +330,10 @@ int daemonFreeClientStream(virNetServerClientPtr client,
if (!stream)
return 0;
+ stream->refs--;
+ if (stream->refs)
+ return 0;
+
VIR_DEBUG("client=%p, proc=%d, serial=%d",
client, stream->procedure, stream->serial);
@@ -727,7 +735,7 @@ daemonStreamHandleRead(virNetServerClientPtr client,
if (msg) {
msg->cb = daemonStreamMessageFinished;
msg->opaque = stream;
- virNetServerClientRef(client);
+ stream->refs++;
ret = virNetServerProgramSendStreamData(remoteProgram,
client,
msg,
--
1.7.6
13 years, 1 month
[libvirt] Python stream callback removal
by Dave Allan
I'm trying to write an example serial console implementation in python
(attached), but I'm having some trouble getting stream events to do
what I want. The console itself works fine as long as the domain
stays up, but as soon as the domain shuts down the python script goes
into a tight loop repeatedly calling the stream event callback.
Debugging indicates that the stream event callback is being requested
to be removed, but it never actually is removed which makes me think I
am not properly releasing some resource, but I was under the
impression that an error on a stream resulting in the stream aborting
was supposed to free all the resources for me. Is that not correct?
Thanks for any help anybody can provide,
Dave
13 years, 1 month
[libvirt] [PATCH] Fix memory leak dispatching domain events
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
When dispatching domain events we will create an XDR struct
containing the event info. Some of this data may be allocated
on the heap and so must be freed. The graphics event dispatcher
had a broken attempt to free one field, but missed others. All
the events have a dom->name string that needs freeing. The code
should have used the xdr_free() procedure for doing all this
* daemon/remote.c: Use xdr_free after dispatching events
---
daemon/remote.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 0f088c6..34c6364 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -334,8 +334,6 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED,
REMOTE_PROC_DOMAIN_EVENT_GRAPHICS,
(xdrproc_t)xdr_remote_domain_event_graphics_msg, &data);
- VIR_FREE(data.subject.subject_val);
-
return 0;
}
@@ -2498,7 +2496,7 @@ remoteDispatchDomainEventSend(virNetServerClientPtr client,
virNetMessagePtr msg;
if (!(msg = virNetMessageNew()))
- return;
+ goto cleanup;
msg->header.prog = virNetServerProgramGetID(program);
msg->header.vers = virNetServerProgramGetVersion(program);
@@ -2516,10 +2514,12 @@ remoteDispatchDomainEventSend(virNetServerClientPtr client,
VIR_DEBUG("Queue event %d %zu", procnr, msg->bufferLength);
virNetServerClientSendMessage(client, msg);
+ xdr_free(proc, data);
return;
cleanup:
virNetMessageFree(msg);
+ xdr_free(proc, data);
}
static int
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] Fix memory leak parsing 'relabel' attribute in domain security XML
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
* src/conf/domain_conf.c: Free the 'relabel' attribute
---
src/conf/domain_conf.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 44212cf..00212db 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5300,8 +5300,10 @@ virSecurityLabelDefParseXML(const virDomainDefPtr def,
} else {
virDomainReportError(VIR_ERR_XML_ERROR,
_("invalid security relabel value %s"), p);
+ VIR_FREE(p);
goto error;
}
+ VIR_FREE(p);
if (def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC &&
def->seclabel.norelabel) {
virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] Stop libxl driver polluting logs on non-Xen hosts
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
If the libxl driver is compiled in, then everytime libvirtd
starts up on a non-Xen Dom0 host, it logs a error message.
Since this is an expected condition, we should not log at
'error' level, only 'info'.
* src/libxl/libxl_driver.c: Lower log level for certain
expected errors during driver init
---
src/libxl/libxl_driver.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index d6e0c28..3cffb5d 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -963,19 +963,19 @@ libxlStartup(int privileged) {
libxl_driver->logger =
(xentoollog_logger *)xtl_createlogger_stdiostream(libxl_driver->logger_file, XTL_DEBUG, 0);
if (!libxl_driver->logger) {
- VIR_ERROR(_("cannot create logger for libxenlight"));
+ VIR_INFO(_("cannot create logger for libxenlight, disabling driver"));
goto fail;
}
if (libxl_ctx_init(&libxl_driver->ctx,
LIBXL_VERSION,
libxl_driver->logger)) {
- VIR_ERROR(_("cannot initialize libxenlight context"));
+ VIR_INFO(_("cannot initialize libxenlight context, probably not running in a Xen Dom0, disabling driver"));
goto fail;
}
if ((ver_info = libxl_get_version_info(&libxl_driver->ctx)) == NULL) {
- VIR_ERROR(_("cannot version information from libxenlight"));
+ VIR_INFO(_("cannot version information from libxenlight, disabling driver"));
goto fail;
}
libxl_driver->version = (ver_info->xen_version_major * 1000000) +
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] Don't leak memory if a cgroup is mounted multiple times
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
It is possible (expected/likely in Fedora 15) for a cgroup controller
to be mounted in multiple locations at the same time, due to bind
mounts. Currently we leak memory if this happens, because we overwrite
the previous 'mountPoint' string. Instead just accept the first match
we find.
* src/util/cgroup.c: Only accept first match for a cgroup
controller mount
---
src/util/cgroup.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/src/util/cgroup.c b/src/util/cgroup.c
index f3ec80f..c8d1f33 100644
--- a/src/util/cgroup.c
+++ b/src/util/cgroup.c
@@ -131,7 +131,12 @@ static int virCgroupDetectMounts(virCgroupPtr group)
} else {
len = strlen(tmp);
}
+ /* NB, the same controller can appear >1 time in mount list
+ * due to bind mounts from one location to another. Pick the
+ * first entry only
+ */
if (typelen == len && STREQLEN(typestr, tmp, len) &&
+ !group->controllers[i].mountPoint &&
!(group->controllers[i].mountPoint = strdup(entry.mnt_dir)))
goto no_memory;
tmp = next;
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] stream: remove redundant reference to client while sending stream data
by Guannan Ren
*daemon/stream.c: remove virNetServerClientRef()
---
daemon/stream.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/daemon/stream.c b/daemon/stream.c
index 7d2b367..0333dfd 100644
--- a/daemon/stream.c
+++ b/daemon/stream.c
@@ -727,7 +727,6 @@ daemonStreamHandleRead(virNetServerClientPtr client,
if (msg) {
msg->cb = daemonStreamMessageFinished;
msg->opaque = stream;
- virNetServerClientRef(client);
ret = virNetServerProgramSendStreamData(remoteProgram,
client,
msg,
--
1.7.1
13 years, 1 month
[libvirt] [PATCH] add sendevent command and related APIs
by Lai Jiangshan
Enable libvirt send some events to the guest.
This command currently only supports NMI and key events.
Signed-off-by: Lai Jiangshan <laijs(a)cn.fujitsu.com>
---
daemon/remote.c | 52 +++++++++++++++++++++
daemon/remote_dispatch_args.h | 2
daemon/remote_dispatch_prototypes.h | 16 ++++++
daemon/remote_dispatch_table.h | 10 ++++
include/libvirt/libvirt.h.in | 3 +
src/driver.h | 7 ++
src/esx/esx_driver.c | 2
src/libvirt.c | 88 ++++++++++++++++++++++++++++++++++++
src/libvirt_public.syms | 2
src/libxl/libxl_driver.c | 2
src/lxc/lxc_driver.c | 2
src/openvz/openvz_driver.c | 2
src/phyp/phyp_driver.c | 4 +
src/qemu/qemu_driver.c | 86 +++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor.c | 27 +++++++++++
src/qemu/qemu_monitor.h | 3 +
src/qemu/qemu_monitor_json.c | 68 +++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 3 +
src/qemu/qemu_monitor_text.c | 56 ++++++++++++++++++++++
src/qemu/qemu_monitor_text.h | 2
src/remote/remote_driver.c | 50 ++++++++++++++++++++
src/remote/remote_protocol.c | 22 +++++++++
src/remote/remote_protocol.h | 19 +++++++
src/remote/remote_protocol.x | 14 +++++
src/test/test_driver.c | 2
src/uml/uml_driver.c | 2
src/vbox/vbox_tmpl.c | 2
src/vmware/vmware_driver.c | 2
src/xen/xen_driver.c | 2
src/xenapi/xenapi_driver.c | 2
tools/virsh.c | 56 ++++++++++++++++++++++
31 files changed, 608 insertions(+), 2 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 1700c2d..5f9e78a 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -2836,6 +2836,58 @@ remoteDispatchDomainGetBlkioParameters(struct qemud_server *server
}
static int
+remoteDispatchDomainSendEventNMI(struct qemud_server *server ATTRIBUTE_UNUSED,
+ struct qemud_client *client ATTRIBUTE_UNUSED,
+ virConnectPtr conn,
+ remote_message_header *hdr ATTRIBUTE_UNUSED,
+ remote_error *rerr,
+ remote_domain_send_event_nmi_args *args,
+ void *ret ATTRIBUTE_UNUSED)
+{
+ virDomainPtr dom;
+
+ dom = get_nonnull_domain(conn, args->dom);
+ if (dom == NULL) {
+ remoteDispatchConnError(rerr, conn);
+ return -1;
+ }
+
+ if (virDomainSendEventNMI(dom, args->vcpu) == -1) {
+ virDomainFree(dom);
+ remoteDispatchConnError(rerr, conn);
+ return -1;
+ }
+ virDomainFree(dom);
+ return 0;
+}
+
+static int
+remoteDispatchDomainSendEventKey(struct qemud_server *server ATTRIBUTE_UNUSED,
+ struct qemud_client *client ATTRIBUTE_UNUSED,
+ virConnectPtr conn,
+ remote_message_header *hdr ATTRIBUTE_UNUSED,
+ remote_error *rerr,
+ remote_domain_send_event_key_args *args,
+ void *ret ATTRIBUTE_UNUSED)
+{
+ virDomainPtr dom;
+
+ dom = get_nonnull_domain(conn, args->dom);
+ if (dom == NULL) {
+ remoteDispatchConnError(rerr, conn);
+ return -1;
+ }
+
+ if (virDomainSendEventKey(dom, args->key) == -1) {
+ virDomainFree(dom);
+ remoteDispatchConnError(rerr, conn);
+ return -1;
+ }
+ virDomainFree(dom);
+ return 0;
+}
+
+static int
remoteDispatchDomainSetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED,
struct qemud_client *client ATTRIBUTE_UNUSED,
virConnectPtr conn,
diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h
index f9537d7..289a42e 100644
--- a/daemon/remote_dispatch_args.h
+++ b/daemon/remote_dispatch_args.h
@@ -178,3 +178,5 @@
remote_domain_migrate_set_max_speed_args val_remote_domain_migrate_set_max_speed_args;
remote_storage_vol_upload_args val_remote_storage_vol_upload_args;
remote_storage_vol_download_args val_remote_storage_vol_download_args;
+ remote_domain_send_event_nmi_args val_remote_domain_send_event_nmi_args;
+ remote_domain_send_event_key_args val_remote_domain_send_event_key_args;
diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h
index 18bf41d..f920193 100644
--- a/daemon/remote_dispatch_prototypes.h
+++ b/daemon/remote_dispatch_prototypes.h
@@ -1618,3 +1618,19 @@ static int remoteDispatchSupportsFeature(
remote_error *err,
remote_supports_feature_args *args,
remote_supports_feature_ret *ret);
+static int remoteDispatchDomainSendEventNMI(
+ struct qemud_server *server,
+ struct qemud_client *client,
+ virConnectPtr conn,
+ remote_message_header *hdr,
+ remote_error *err,
+ remote_domain_send_event_nmi_args *args,
+ void *ret);
+static int remoteDispatchDomainSendEventKey(
+ struct qemud_server *server,
+ struct qemud_client *client,
+ virConnectPtr conn,
+ remote_message_header *hdr,
+ remote_error *err,
+ remote_domain_send_event_key_args *args,
+ void *ret);
diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h
index b39f7c2..a706b19 100644
--- a/daemon/remote_dispatch_table.h
+++ b/daemon/remote_dispatch_table.h
@@ -1052,3 +1052,13 @@
.args_filter = (xdrproc_t) xdr_remote_storage_vol_download_args,
.ret_filter = (xdrproc_t) xdr_void,
},
+{ /* DomainSendEventNmi => 210 */
+ .fn = (dispatch_fn) remoteDispatchDomainSendEventNMI,
+ .args_filter = (xdrproc_t) xdr_remote_domain_send_event_nmi_args,
+ .ret_filter = (xdrproc_t) xdr_void,
+},
+{ /* DomainSendEventKey => 211 */
+ .fn = (dispatch_fn) remoteDispatchDomainSendEventKey,
+ .args_filter = (xdrproc_t) xdr_remote_domain_send_event_key_args,
+ .ret_filter = (xdrproc_t) xdr_void,
+},
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index bd36015..adbe482 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2517,6 +2517,9 @@ int virDomainOpenConsole(virDomainPtr dom,
virStreamPtr st,
unsigned int flags);
+int virDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu);
+int virDomainSendEventKey(virDomainPtr domain, const char *key);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/driver.h b/src/driver.h
index e5f91ca..6caf13f 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -515,6 +515,11 @@ typedef int
virStreamPtr st,
unsigned int flags);
+typedef int
+ (*virDrvDomainSendEventNMI)(virDomainPtr dom, unsigned int vcpu);
+
+typedef int
+ (*virDrvDomainSendEventKey)(virDomainPtr dom, const char *key);
/**
* _virDriver:
@@ -639,6 +644,8 @@ struct _virDriver {
virDrvDomainSnapshotDelete domainSnapshotDelete;
virDrvQemuDomainMonitorCommand qemuDomainMonitorCommand;
virDrvDomainOpenConsole domainOpenConsole;
+ virDrvDomainSendEventNMI domainSendEventNMI;
+ virDrvDomainSendEventKey domainSendEventKey;
};
typedef int
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index deda372..7167712 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -4675,6 +4675,8 @@ static virDriver esxDriver = {
esxDomainSnapshotDelete, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
diff --git a/src/libvirt.c b/src/libvirt.c
index 9bdb4c8..245247f 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -5245,6 +5245,94 @@ error:
}
/**
+ * virDomainSendEvnetNMI:
+ * @domain: pointer to domain object, or NULL for Domain0
+ * @vcpu: the virtual CPU id to send NMI to
+ *
+ * Send NMI to a special vcpu of the guest
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+
+int virDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu)
+{
+ virConnectPtr conn;
+ VIR_DOMAIN_DEBUG(domain, "vcpu=%u", vcpu);
+
+ virResetLastError();
+
+ if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+ virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return (-1);
+ }
+ if (domain->conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ goto error;
+ }
+
+ conn = domain->conn;
+
+ if (conn->driver->domainSendEventNMI) {
+ int ret;
+ ret = conn->driver->domainSendEventNMI(domain, vcpu);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(domain->conn);
+ return -1;
+}
+
+/**
+ * virDomainSendEventKey:
+ * @domain: pointer to domain object, or NULL for Domain0
+ * @key: the string of key or key sequence
+ *
+ * Send a special key or key sequence to the guest
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+
+int virDomainSendEventKey(virDomainPtr domain, const char *key)
+{
+ virConnectPtr conn;
+ VIR_DOMAIN_DEBUG(domain, "key=%s", key);
+
+ virResetLastError();
+
+ if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+ virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return (-1);
+ }
+ if (domain->conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ goto error;
+ }
+
+ conn = domain->conn;
+
+ if (conn->driver->domainSendEventKey) {
+ int ret;
+ ret = conn->driver->domainSendEventKey(domain, key);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(domain->conn);
+ return -1;
+}
+
+/**
* virDomainSetVcpus:
* @domain: pointer to domain object, or NULL for Domain0
* @nvcpus: the new number of virtual CPUs for this domain
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index b4aed41..cd0f474 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -434,6 +434,8 @@ LIBVIRT_0.9.0 {
virEventRunDefaultImpl;
virStorageVolDownload;
virStorageVolUpload;
+ virDomainSendEventNMI;
+ virDomainSendEventKey;
} LIBVIRT_0.8.8;
# .... define new API here using predicted next version number ....
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index e996ff6..040fc16 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -2353,6 +2353,8 @@ static virDriver libxlDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
static virStateDriver libxlStateDriver = {
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index e905302..1284ab1 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -2906,6 +2906,8 @@ static virDriver lxcDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
lxcDomainOpenConsole, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
static virStateDriver lxcStateDriver = {
diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c
index fb30c37..26ba0a5 100644
--- a/src/openvz/openvz_driver.c
+++ b/src/openvz/openvz_driver.c
@@ -1654,6 +1654,8 @@ static virDriver openvzDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
int openvzRegister(void) {
diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c
index 51f9ff6..d5d0ea6 100644
--- a/src/phyp/phyp_driver.c
+++ b/src/phyp/phyp_driver.c
@@ -4054,7 +4054,9 @@ static virDriver phypDriver = {
NULL, /* domainRevertToSnapshot */
NULL, /* domainSnapshotDelete */
NULL, /* qemuMonitorCommand */
- NULL, /* domainOpenConsole */
+ NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
static virStorageDriver phypStorageDriver = {
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dd12dc8..02af591 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1659,6 +1659,90 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) {
return qemudDomainSetMemoryFlags(dom, newmem, VIR_DOMAIN_MEM_LIVE);
}
+static int qemuDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu)
+{
+ struct qemud_driver *driver = domain->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(domain->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ priv = vm->privateData;
+
+ if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
+ goto cleanup;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorSendEventNMI(priv->mon, vcpu);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (qemuDomainObjEndJob(vm) == 0) {
+ vm = NULL;
+ goto cleanup;
+ }
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
+static int qemuDomainSendEventKey(virDomainPtr domain, const char *key)
+{
+ struct qemud_driver *driver = domain->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(domain->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ priv = vm->privateData;
+
+ if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
+ goto cleanup;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorSendEventKey(priv->mon, key);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (qemuDomainObjEndJob(vm) == 0) {
+ vm = NULL;
+ goto cleanup;
+ }
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
static int qemudDomainGetInfo(virDomainPtr dom,
virDomainInfoPtr info) {
struct qemud_driver *driver = dom->conn->privateData;
@@ -6923,6 +7007,8 @@ static virDriver qemuDriver = {
qemuDomainSnapshotDelete, /* domainSnapshotDelete */
qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
qemuDomainOpenConsole, /* domainOpenConsole */
+ qemuDomainSendEventNMI, /* domainSendEventNMI */
+ qemuDomainSendEventKey, /* domainSendEventKey */
};
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 2d28f8d..bc2e269 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2228,3 +2228,30 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply);
return ret;
}
+
+
+int qemuMonitorSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu)
+{
+ int ret;
+
+ VIR_DEBUG("mon=%p, vcpu=%u", mon, vcpu);
+
+ if (mon->json)
+ ret = qemuMonitorJSONSendEventNMI(mon, vcpu);
+ else
+ ret = qemuMonitorTextSendEventNMI(mon, vcpu);
+ return ret;
+}
+
+int qemuMonitorSendEventKey(qemuMonitorPtr mon, const char *key)
+{
+ int ret;
+
+ VIR_DEBUG("mon=%p, key sequence=%s", mon, key);
+
+ if (mon->json)
+ ret = qemuMonitorJSONSendEventKey(mon, key);
+ else
+ ret = qemuMonitorTextSendEventKey(mon, key);
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index c90219b..fdc9859 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -423,6 +423,9 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
char **reply,
bool hmp);
+int qemuMonitorSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu);
+int qemuMonitorSendEventKey(qemuMonitorPtr mon, const char *key);
+
/**
* When running two dd process and using <> redirection, we need a
* shell that will not truncate files. These two strings serve that
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 20a78e1..5149d9e 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -2513,3 +2513,71 @@ cleanup:
return ret;
}
+
+int qemuMonitorJSONSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ char *hmp_cmd;
+
+ /*
+ * FIXME: qmp nmi is not supported until qemu-0.16.0,
+ * use human-monitor-command instead temporary.
+ */
+ if (virAsprintf(&hmp_cmd, "nmi %u", vcpu) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ cmd = qemuMonitorJSONMakeCommand("human-monitor-command",
+ "s:command-line", hmp_cmd,
+ NULL);
+ if (!cmd)
+ goto out_free_hmp_cmd;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0)
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+out_free_hmp_cmd:
+ VIR_FREE(hmp_cmd);
+ return ret;
+}
+
+int qemuMonitorJSONSendEventKey(qemuMonitorPtr mon, const char *key_seq)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ char *hmp_cmd;
+
+ /*
+ * FIXME: qmp sendkey is not supported until qemu-0.16.0,
+ * use human-monitor-command instead temporary.
+ */
+ if (virAsprintf(&hmp_cmd, "sendkey %s", key_seq) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ cmd = qemuMonitorJSONMakeCommand("human-monitor-command",
+ "s:command-line", hmp_cmd,
+ NULL);
+ if (!cmd)
+ goto out_free_hmp_cmd;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0)
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+out_free_hmp_cmd:
+ VIR_FREE(hmp_cmd);
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 086f0e1..dc206df 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -204,4 +204,7 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
char **reply_str,
bool hmp);
+int qemuMonitorJSONSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu);
+int qemuMonitorJSONSendEventKey(qemuMonitorPtr mon, const char *key);
+
#endif /* QEMU_MONITOR_JSON_H */
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index e0e3292..d3416a8 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -2627,3 +2627,59 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
return ret;
}
+
+int qemuMonitorTextSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu)
+{
+ char *cmd;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&cmd, "nmi %u", vcpu) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+ if (qemuMonitorTextArbitraryCommand(mon, cmd, &reply)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("failed to send NMI using command '%s'"),
+ cmd);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cmd);
+ VIR_FREE(reply);
+ return ret;
+}
+
+int qemuMonitorTextSendEventKey(qemuMonitorPtr mon, const char *key)
+{
+ char *cmd;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&cmd, "sendkey %s", key) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+ if (qemuMonitorTextArbitraryCommand(mon, cmd, &reply)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("failed to send key using command '%s'"),
+ cmd);
+ goto cleanup;
+ }
+
+ if (strstr(reply, "unknown key") != NULL) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("sent unknown key"));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cmd);
+ VIR_FREE(reply);
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 0838a2b..4a03c40 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -198,4 +198,6 @@ int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name);
int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
char **reply);
+int qemuMonitorTextSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu);
+int qemuMonitorTextSendEventKey(qemuMonitorPtr mon, const char *key);
#endif /* QEMU_MONITOR_TEXT_H */
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index bf94e70..676f473 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -2929,6 +2929,54 @@ done:
}
static int
+remoteDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu)
+{
+ int rv = -1;
+ remote_domain_send_event_nmi_args args;
+ struct private_data *priv = domain->conn->privateData;
+
+ remoteDriverLock(priv);
+
+ make_nonnull_domain (&args.dom, domain);
+ args.vcpu = vcpu;
+
+ if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SEND_EVENT_NMI,
+ (xdrproc_t) xdr_remote_domain_send_event_nmi_args, (char *) &args,
+ (xdrproc_t) xdr_void, (char *) NULL) == -1)
+ goto done;
+
+ rv = 0;
+
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+static int
+remoteDomainSendEventKey(virDomainPtr domain, const char *key)
+{
+ int rv = -1;
+ remote_domain_send_event_key_args args;
+ struct private_data *priv = domain->conn->privateData;
+
+ remoteDriverLock(priv);
+
+ make_nonnull_domain (&args.dom, domain);
+ args.key = (char *)key;
+
+ if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SEND_EVENT_KEY,
+ (xdrproc_t) xdr_remote_domain_send_event_key_args, (char *) &args,
+ (xdrproc_t) xdr_void, (char *) NULL) == -1)
+ goto done;
+
+ rv = 0;
+
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+static int
remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus)
{
int rv = -1;
@@ -11296,6 +11344,8 @@ static virDriver remote_driver = {
remoteDomainSnapshotDelete, /* domainSnapshotDelete */
remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
remoteDomainOpenConsole, /* domainOpenConsole */
+ remoteDomainSendEventNMI, /* domainSendEventNMI */
+ remoteDomainSendEventKey, /* domainSendEventKey */
};
static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c
index 5604371..a829219 100644
--- a/src/remote/remote_protocol.c
+++ b/src/remote/remote_protocol.c
@@ -1463,6 +1463,28 @@ xdr_remote_domain_undefine_args (XDR *xdrs, remote_domain_undefine_args *objp)
}
bool_t
+xdr_remote_domain_send_event_nmi_args (XDR *xdrs, remote_domain_send_event_nmi_args *objp)
+{
+
+ if (!xdr_remote_nonnull_domain (xdrs, &objp->dom))
+ return FALSE;
+ if (!xdr_u_int (xdrs, &objp->vcpu))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_remote_domain_send_event_key_args (XDR *xdrs, remote_domain_send_event_key_args *objp)
+{
+
+ if (!xdr_remote_nonnull_domain (xdrs, &objp->dom))
+ return FALSE;
+ if (!xdr_remote_nonnull_string (xdrs, &objp->key))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
xdr_remote_domain_set_vcpus_args (XDR *xdrs, remote_domain_set_vcpus_args *objp)
{
diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h
index d9bf151..027ef88 100644
--- a/src/remote/remote_protocol.h
+++ b/src/remote/remote_protocol.h
@@ -2200,6 +2200,19 @@ struct remote_storage_vol_download_args {
u_int flags;
};
typedef struct remote_storage_vol_download_args remote_storage_vol_download_args;
+
+struct remote_domain_send_event_nmi_args {
+ remote_nonnull_domain dom;
+ u_int vcpu;
+};
+typedef struct remote_domain_send_event_nmi_args remote_domain_send_event_nmi_args;
+
+struct remote_domain_send_event_key_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string key;
+};
+typedef struct remote_domain_send_event_key_args remote_domain_send_event_key_args;
+
#define REMOTE_PROGRAM 0x20008086
#define REMOTE_PROTOCOL_VERSION 1
@@ -2413,6 +2426,8 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207,
REMOTE_PROC_STORAGE_VOL_UPLOAD = 208,
REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209,
+ REMOTE_PROC_DOMAIN_SEND_EVENT_NMI = 210,
+ REMOTE_PROC_DOMAIN_SEND_EVENT_KEY = 211,
};
typedef enum remote_procedure remote_procedure;
@@ -2561,6 +2576,8 @@ extern bool_t xdr_remote_domain_create_with_flags_ret (XDR *, remote_domain_cre
extern bool_t xdr_remote_domain_define_xml_args (XDR *, remote_domain_define_xml_args*);
extern bool_t xdr_remote_domain_define_xml_ret (XDR *, remote_domain_define_xml_ret*);
extern bool_t xdr_remote_domain_undefine_args (XDR *, remote_domain_undefine_args*);
+extern bool_t xdr_remote_domain_send_event_nmi_args (XDR *, remote_domain_send_event_nmi_args*);
+extern bool_t xdr_remote_domain_send_event_key_args (XDR *, remote_domain_send_event_key_args*);
extern bool_t xdr_remote_domain_set_vcpus_args (XDR *, remote_domain_set_vcpus_args*);
extern bool_t xdr_remote_domain_set_vcpus_flags_args (XDR *, remote_domain_set_vcpus_flags_args*);
extern bool_t xdr_remote_domain_get_vcpus_flags_args (XDR *, remote_domain_get_vcpus_flags_args*);
@@ -2918,6 +2935,8 @@ extern bool_t xdr_remote_domain_create_with_flags_ret ();
extern bool_t xdr_remote_domain_define_xml_args ();
extern bool_t xdr_remote_domain_define_xml_ret ();
extern bool_t xdr_remote_domain_undefine_args ();
+extern bool_t xdr_remote_domain_send_event_nmi_args ();
+extern bool_t xdr_remote_domain_send_event_key_args ();
extern bool_t xdr_remote_domain_set_vcpus_args ();
extern bool_t xdr_remote_domain_set_vcpus_flags_args ();
extern bool_t xdr_remote_domain_get_vcpus_flags_args ();
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 675eccd..34600d7 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -817,6 +817,16 @@ struct remote_domain_undefine_args {
remote_nonnull_domain dom;
};
+struct remote_domain_send_event_nmi_args {
+ remote_nonnull_domain dom;
+ unsigned int vcpu;
+};
+
+struct remote_domain_send_event_key_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string key;
+};
+
struct remote_domain_set_vcpus_args {
remote_nonnull_domain dom;
int nvcpus;
@@ -2176,8 +2186,10 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_GET_BLKIO_PARAMETERS = 206,
REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207,
REMOTE_PROC_STORAGE_VOL_UPLOAD = 208,
- REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209
+ REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209,
+ REMOTE_PROC_DOMAIN_SEND_EVENT_NMI = 210,
+ REMOTE_PROC_DOMAIN_SEND_EVENT_KEY = 211,
/*
* Notice how the entries are grouped in sets of 10 ?
* Nice isn't it. Please keep it this way when adding more.
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index 17f5ad9..2163850 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -5447,6 +5447,8 @@ static virDriver testDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
static virNetworkDriver testNetworkDriver = {
diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c
index e2bd5f2..756877d 100644
--- a/src/uml/uml_driver.c
+++ b/src/uml/uml_driver.c
@@ -2249,6 +2249,8 @@ static virDriver umlDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
umlDomainOpenConsole, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
static int
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 8bd27dd..73c5c87 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -8647,6 +8647,8 @@ virDriver NAME(Driver) = {
vboxDomainSnapshotDelete, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
virNetworkDriver NAME(NetworkDriver) = {
diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c
index b5e416b..eb64087 100644
--- a/src/vmware/vmware_driver.c
+++ b/src/vmware/vmware_driver.c
@@ -1007,6 +1007,8 @@ static virDriver vmwareDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
int
diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c
index 9f47722..bd82001 100644
--- a/src/xen/xen_driver.c
+++ b/src/xen/xen_driver.c
@@ -2141,6 +2141,8 @@ static virDriver xenUnifiedDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
xenUnifiedDomainOpenConsole, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
/**
diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c
index 27206a0..0f85ad8 100644
--- a/src/xenapi/xenapi_driver.c
+++ b/src/xenapi/xenapi_driver.c
@@ -1885,6 +1885,8 @@ static virDriver xenapiDriver = {
NULL, /* domainSnapshotDelete */
NULL, /* qemuDomainMonitorCommand */
NULL, /* domainOpenConsole */
+ NULL, /* domainSendEventNMI */
+ NULL, /* domainSendEventKey */
};
/**
diff --git a/tools/virsh.c b/tools/virsh.c
index faeaf47..0b78c6d 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -2910,6 +2910,61 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
}
/*
+ * "sendevent" command
+ */
+static const vshCmdInfo info_sendevent[] = {
+ {"help", N_("send events to the guest")},
+ {"desc", N_("Send events (NMI or Keys) to the guest domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_sendevent[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"eventtype", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the type of event (nmi or key)")},
+ {"eventcontent", VSH_OT_DATA, VSH_OFLAG_REQ, N_("content for the event.")},
+ {NULL, 0, 0, NULL}
+};
+
+
+static int
+cmdSendEvent(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *type;
+ int ret = TRUE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return FALSE;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return FALSE;
+
+ if (vshCommandOptString(cmd, "eventtype", &type) < 0)
+ return FALSE;
+
+ if (STREQ(type, "nmi")) {
+ int cpu;
+
+ if ((vshCommandOptInt(cmd, "eventcontent", &cpu) < 0)
+ || (virDomainSendEventNMI(dom, cpu) < 0))
+ ret = FALSE;
+ } else if (STREQ(type, "key")) {
+ const char *key;
+
+ if ((vshCommandOptString(cmd, "eventcontent", &key) < 0)
+ || (virDomainSendEventKey(dom, key) < 0))
+ ret = FALSE;
+ } else {
+ virDomainFree(dom);
+ vshError(ctl, _("Invalid event type: %s, only \"nmi\" or \"key\" supported currently."), type);
+ return FALSE;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
* "setmemory" command
*/
static const vshCmdInfo info_setmem[] = {
@@ -10693,6 +10748,7 @@ static const vshCmdDef domManagementCmds[] = {
{"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem},
{"setmem", cmdSetmem, opts_setmem, info_setmem},
{"setvcpus", cmdSetvcpus, opts_setvcpus, info_setvcpus},
+ {"sendevent", cmdSendEvent, opts_sendevent, info_sendevent},
{"shutdown", cmdShutdown, opts_shutdown, info_shutdown},
{"start", cmdStart, opts_start, info_start},
{"suspend", cmdSuspend, opts_suspend, info_suspend},
13 years, 1 month