[libvirt] [PATCH v3 4/5] hyperv: support virDomainSendKey

Sri Ramanujam posted 5 patches 8 years, 9 months ago
There is a newer version of this series
[libvirt] [PATCH v3 4/5] hyperv: support virDomainSendKey
Posted by Sri Ramanujam 8 years, 9 months ago
This commit adds support for virDomainSendKey. It also serves as an
example of how to use the new method invocation APIs with a single
"simple" type parameter.
---
 src/hyperv/hyperv_driver.c            | 85 ++++++++++++++++++++++++++++++++++
 src/hyperv/hyperv_wmi.c               |  7 +++
 src/hyperv/hyperv_wmi.h               |  3 +-
 src/hyperv/hyperv_wmi_generator.input | 86 +++++++++++++++++++++++++++++++++++
 4 files changed, 180 insertions(+), 1 deletion(-)

diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c
index 0ca5971..9562d5a 100644
--- a/src/hyperv/hyperv_driver.c
+++ b/src/hyperv/hyperv_driver.c
@@ -35,6 +35,7 @@
 #include "hyperv_wmi.h"
 #include "openwsman.h"
 #include "virstring.h"
+#include "virkeycode.h"
 
 #define VIR_FROM_THIS VIR_FROM_HYPERV
 
@@ -1373,6 +1374,89 @@ hypervConnectListAllDomains(virConnectPtr conn,
 #undef MATCH
 
 
+static int
+hypervDomainSendKey(virDomainPtr domain, unsigned int codeset,
+        unsigned int holdtime ATTRIBUTE_UNUSED, unsigned int *keycodes,
+        int nkeycodes, unsigned int flags)
+{
+    int result = -1;
+    size_t i = 0;
+    int keycode = 0;
+    int *translatedKeycodes = NULL;
+    hypervPrivate *priv = domain->conn->privateData;
+    char uuid_string[VIR_UUID_STRING_BUFLEN];
+    char *selector = NULL;
+    Msvm_ComputerSystem *computerSystem = NULL;
+    Msvm_Keyboard *keyboard = NULL;
+    virBuffer query = VIR_BUFFER_INITIALIZER;
+    hypervInvokeParamsListPtr params = NULL;
+
+    virCheckFlags(0, -1);
+
+    virUUIDFormat(domain->uuid, uuid_string);
+
+    if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
+        goto cleanup;
+
+    virBufferAsprintf(&query,
+            "associators of "
+            "{Msvm_ComputerSystem.CreationClassName=\"Msvm_ComputerSystem\","
+            "Name=\"%s\"} "
+            "where ResultClass = Msvm_Keyboard",
+            uuid_string);
+
+
+    if (hypervGetMsvmKeyboardList(priv, &query, &keyboard) < 0)
+        goto cleanup;
+
+    /* translate keycodes to xt and generate keyup scancodes. */
+    translatedKeycodes = (int *) keycodes;
+    for (i = 0; i < nkeycodes; i++) {
+        if (codeset != VIR_KEYCODE_SET_WIN32) {
+            keycode = virKeycodeValueTranslate(codeset, VIR_KEYCODE_SET_WIN32,
+                    translatedKeycodes[i]);
+
+            if (keycode < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("Could not translate keycode"));
+                goto cleanup;
+            }
+            translatedKeycodes[i] = keycode;
+        }
+    }
+
+    if (virAsprintf(&selector,
+                "CreationClassName=Msvm_Keyboard&DeviceID=%s&"
+                "SystemCreationClassName=Msvm_ComputerSystem&"
+                "SystemName=%s", keyboard->data.common->DeviceID, uuid_string) < 0)
+        goto cleanup;
+
+    /* type the keys */
+    for (i = 0; i < nkeycodes; i++) {
+        char keycodeStr[sizeof(int) * 3 + 2];
+        snprintf(keycodeStr, sizeof(keycodeStr), "%d", translatedKeycodes[i]);
+
+        /* params obj takes ownership of selector */
+        params = hypervInitInvokeParamsList(priv, "TypeKey", selector,
+                Msvm_Keyboard_WmiInfo);
+        if (hypervAddSimpleParam(params, "keyCode", keycodeStr) < 0)
+            goto cleanup;
+
+        if (hypervInvokeMethod(priv, params, NULL) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not press key %d"),
+                           translatedKeycodes[i]);
+            goto cleanup;
+        }
+    }
+
+    result = 0;
+
+ cleanup:
+    hypervFreeObject(priv, (hypervObject *) keyboard);
+    hypervFreeObject(priv, (hypervObject *) computerSystem);
+    virBufferFreeAndReset(&query);
+    return result;
+}
 
 
 static virHypervisorDriver hypervHypervisorDriver = {
@@ -1408,6 +1492,7 @@ static virHypervisorDriver hypervHypervisorDriver = {
     .domainManagedSave = hypervDomainManagedSave, /* 0.9.5 */
     .domainHasManagedSaveImage = hypervDomainHasManagedSaveImage, /* 0.9.5 */
     .domainManagedSaveRemove = hypervDomainManagedSaveRemove, /* 0.9.5 */
+    .domainSendKey = hypervDomainSendKey, /* TODO: version */
     .connectIsAlive = hypervConnectIsAlive, /* 0.9.8 */
 };
 
diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c
index deea907..df248e0 100644
--- a/src/hyperv/hyperv_wmi.c
+++ b/src/hyperv/hyperv_wmi.c
@@ -1323,6 +1323,13 @@ hypervGetMsvmMemorySettingDataList(hypervPrivate *priv, virBufferPtr query,
                                  (hypervObject **) list);
 }
 
+int hypervGetMsvmKeyboardList(hypervPrivate *priv, virBufferPtr query,
+                              Msvm_Keyboard **list)
+{
+    return hypervGetWmiClassList(priv, Msvm_Keyboard_WmiInfo, query,
+                                 (hypervObject **) list);
+}
+
 
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h
index 4196f51..740bdcb 100644
--- a/src/hyperv/hyperv_wmi.h
+++ b/src/hyperv/hyperv_wmi.h
@@ -217,7 +217,8 @@ int hypervGetMsvmProcessorSettingDataList(hypervPrivate *priv,
 int hypervGetMsvmMemorySettingDataList(hypervPrivate *priv, virBufferPtr query,
                                        Msvm_MemorySettingData **list);
 
-
+int hypervGetMsvmKeyboardList(hypervPrivate *priv, virBufferPtr query,
+                                       Msvm_Keyboard **list);
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Msvm_ComputerSystem
diff --git a/src/hyperv/hyperv_wmi_generator.input b/src/hyperv/hyperv_wmi_generator.input
index d7f819e..4ccda04 100644
--- a/src/hyperv/hyperv_wmi_generator.input
+++ b/src/hyperv/hyperv_wmi_generator.input
@@ -956,3 +956,89 @@ class Msvm_VirtualHardDiskSettingData
     uint32   PhysicalSectorSize
     string   VirtualDiskId
 end
+
+class Msvm_Keyboard
+    string   Caption
+    string   Description
+    string   ElementName
+    datetime InstallDate
+    string   Name
+    uint16   OperationalStatus[]
+    string   StatusDescriptions[]
+    string   Status
+    uint16   HealthState
+    uint16   EnabledState
+    string   OtherEnabledState
+    uint16   RequestedState
+    uint16   EnabledDefault
+    datetime TimeOfLastStateChange
+    string   SystemCreationClassName
+    string   SystemName
+    string   CreationClassName
+    string   DeviceID
+    boolean  PowerManagementSupported
+    uint16   PowerManagementCapabilities[]
+    uint16   Availability
+    uint16   StatusInfo
+    uint32   LastErrorCode
+    string   ErrorDescription
+    boolean  ErrorCleared
+    string   OtherIdentifyingInfo[]
+    uint64   PowerOnHours
+    uint64   TotalPowerOnHours
+    string   IdentifyingDescriptions[]
+    uint16   AdditionalAvailability[]
+    uint64   MaxQuiesceTime
+    uint16   LocationIndicator
+    boolean  IsLocked
+    string   Layout
+    uint16   NumberOfFunctionKeys
+    uint16   Password
+end
+
+
+class v2/Msvm_Keyboard
+    string   InstanceID
+    string   Caption
+    string   Description
+    string   ElementName
+    datetime InstallDate
+    string   Name
+    uint16   OperationalStatus[]
+    string   StatusDescriptions[]
+    string   Status
+    uint16   HealthState
+    uint16   CommunicationStatus
+    uint16   DetailedStatus
+    uint16   OperatingStatus
+    uint16   PrimaryStatus
+    uint16   EnabledState
+    string   OtherEnabledState
+    uint16   RequestedState
+    uint16   EnabledDefault
+    datetime TimeOfLastStateChange
+    uint16   AvailableRequestedStates[]
+    uint16   TransitioningToState
+    string   SystemCreationClassName
+    string   SystemName
+    string   CreationClassName
+    string   DeviceID
+    boolean  PowerManagementSupported
+    uint16   PowerManagementCapabilities[]
+    uint16   Availability
+    uint16   StatusInfo
+    uint32   LastErrorCode
+    string   ErrorDescription
+    boolean  ErrorCleared
+    string   OtherIdentifyingInfo[]
+    uint64   PowerOnHours
+    uint64   TotalPowerOnHours
+    string   IdentifyingDescriptions[]
+    uint16   AdditionalAvailability[]
+    uint64   MaxQuiesceTime
+    boolean  IsLocked
+    string   Layout
+    uint16   NumberOfFunctionKeys
+    uint16   Password
+    boolean  UnicodeSupported
+end
-- 
2.9.3

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v3 4/5] hyperv: support virDomainSendKey
Posted by Matthias Bolte 8 years, 9 months ago
2017-04-24 20:19 GMT+02:00 Sri Ramanujam <sramanujam@datto.com>:
> This commit adds support for virDomainSendKey. It also serves as an
> example of how to use the new method invocation APIs with a single
> "simple" type parameter.
> ---
>  src/hyperv/hyperv_driver.c            | 85 ++++++++++++++++++++++++++++++++++
>  src/hyperv/hyperv_wmi.c               |  7 +++
>  src/hyperv/hyperv_wmi.h               |  3 +-
>  src/hyperv/hyperv_wmi_generator.input | 86 +++++++++++++++++++++++++++++++++++
>  4 files changed, 180 insertions(+), 1 deletion(-)
>
> diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c
> index 0ca5971..9562d5a 100644
> --- a/src/hyperv/hyperv_driver.c
> +++ b/src/hyperv/hyperv_driver.c
> @@ -35,6 +35,7 @@
>  #include "hyperv_wmi.h"
>  #include "openwsman.h"
>  #include "virstring.h"
> +#include "virkeycode.h"
>
>  #define VIR_FROM_THIS VIR_FROM_HYPERV
>
> @@ -1373,6 +1374,89 @@ hypervConnectListAllDomains(virConnectPtr conn,
>  #undef MATCH
>
>
> +static int
> +hypervDomainSendKey(virDomainPtr domain, unsigned int codeset,
> +        unsigned int holdtime ATTRIBUTE_UNUSED, unsigned int *keycodes,
> +        int nkeycodes, unsigned int flags)
> +{
> +    int result = -1;
> +    size_t i = 0;
> +    int keycode = 0;
> +    int *translatedKeycodes = NULL;
> +    hypervPrivate *priv = domain->conn->privateData;
> +    char uuid_string[VIR_UUID_STRING_BUFLEN];
> +    char *selector = NULL;
> +    Msvm_ComputerSystem *computerSystem = NULL;
> +    Msvm_Keyboard *keyboard = NULL;
> +    virBuffer query = VIR_BUFFER_INITIALIZER;
> +    hypervInvokeParamsListPtr params = NULL;
> +
> +    virCheckFlags(0, -1);
> +
> +    virUUIDFormat(domain->uuid, uuid_string);
> +
> +    if (hypervMsvmComputerSystemFromDomain(domain, &computerSystem) < 0)
> +        goto cleanup;
> +
> +    virBufferAsprintf(&query,
> +            "associators of "
> +            "{Msvm_ComputerSystem.CreationClassName=\"Msvm_ComputerSystem\","
> +            "Name=\"%s\"} "
> +            "where ResultClass = Msvm_Keyboard",
> +            uuid_string);
> +
> +

Unnecessary extra empty line.

> +    if (hypervGetMsvmKeyboardList(priv, &query, &keyboard) < 0)
> +        goto cleanup;
> +
> +    /* translate keycodes to xt and generate keyup scancodes. */
> +    translatedKeycodes = (int *) keycodes;

You cannot translate the keycodes in-place. For the VBox and QEMU
drivers this is kind of okay, because they are always behind the
remote driver. But for the Hyper-V driver this is different. You're
directly modifying user provided memory here. This is not okay, you
need to make a copy of the keycodes before translating them.

> +    for (i = 0; i < nkeycodes; i++) {
> +        if (codeset != VIR_KEYCODE_SET_WIN32) {
> +            keycode = virKeycodeValueTranslate(codeset, VIR_KEYCODE_SET_WIN32,
> +                    translatedKeycodes[i]);
> +
> +            if (keycode < 0) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                        _("Could not translate keycode"));
> +                goto cleanup;
> +            }
> +            translatedKeycodes[i] = keycode;
> +        }
> +    }
> +
> +    if (virAsprintf(&selector,
> +                "CreationClassName=Msvm_Keyboard&DeviceID=%s&"
> +                "SystemCreationClassName=Msvm_ComputerSystem&"
> +                "SystemName=%s", keyboard->data.common->DeviceID, uuid_string) < 0)
> +        goto cleanup;
> +
> +    /* type the keys */
> +    for (i = 0; i < nkeycodes; i++) {
> +        char keycodeStr[sizeof(int) * 3 + 2];

Use "char keycodeStr[INT_BUFSIZE_BOUND(int)];" instead.

> +        snprintf(keycodeStr, sizeof(keycodeStr), "%d", translatedKeycodes[i]);
> +
> +        /* params obj takes ownership of selector */
> +        params = hypervInitInvokeParamsList(priv, "TypeKey", selector,
> +                Msvm_Keyboard_WmiInfo);
> +        if (hypervAddSimpleParam(params, "keyCode", keycodeStr) < 0)
> +            goto cleanup;
> +
> +        if (hypervInvokeMethod(priv, params, NULL) < 0) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not press key %d"),
> +                           translatedKeycodes[i]);
> +            goto cleanup;
> +        }

This is not correct. If virDomainSendKey is called with more than one
keycode than all those keys have to be pressed at once. This allows to
send key combination e.g. Alt+F4. But the way you invoke the TypeKey
function here will just send them one after another. Also you
currently don't handle holdtime.

I think you can make both things work by using
PressKey/Sleep/ReleaseKey instead of TypeKey. See how the VBox driver
does it:

http://libvirt.org/git/?p=libvirt.git;a=blob;f=src/vbox/vbox_common.c#l7551


-- 
Matthias Bolte
http://photron.blogspot.com

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list