[libvirt] [PATCH] add sendevent command and related APIs

Lai Jiangshan laijs at cn.fujitsu.com
Fri Apr 1 09:51:06 UTC 2011


Enable libvirt send some events to the guest.
This command currently only supports NMI and key events.

Signed-off-by: Lai Jiangshan <laijs at 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},




More information about the libvir-list mailing list