qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object


From: Don Slutz
Subject: [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object.
Date: Fri, 30 Jan 2015 16:06:24 -0500

This adds two new inject commands:

inject-vmport-reboot
inject-vmport-halt

And three guest info commands:

vmport-guestinfo-set
vmport-guestinfo-get
query-vmport-guestinfo

More details in qmp-commands.hx

Signed-off-by: Don Slutz <address@hidden>
---
 hw/misc/vmport_rpc.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi-schema.json     |  95 ++++++++++++++++++
 qmp-commands.hx      | 141 ++++++++++++++++++++++++++
 3 files changed, 511 insertions(+)

diff --git a/hw/misc/vmport_rpc.c b/hw/misc/vmport_rpc.c
index e12f912..74cc95e 100644
--- a/hw/misc/vmport_rpc.c
+++ b/hw/misc/vmport_rpc.c
@@ -218,6 +218,56 @@ typedef struct {
     uint32_t edi;
 } vregs;
 
+/*
+ * Run func() for every VMPortRpc device, traverse the tree for
+ * everything else.  Note: This routine expects that opaque is a
+ * VMPortRpcFind pointer and not NULL.
+ */
+static int find_VMPortRpc_device(Object *obj, void *opaque)
+{
+    VMPortRpcFind *find = opaque;
+    Object *dev;
+    VMPortRpcState *s;
+
+    if (find->found) {
+        return 0;
+    }
+    dev = object_dynamic_cast(obj, TYPE_VMPORT_RPC);
+    s = (VMPortRpcState *)dev;
+
+    if (!s) {
+        /* Container, traverse it for children */
+        return object_child_foreach(obj, find_VMPortRpc_device, opaque);
+    }
+
+    find->found++;
+    find->rc = find->func(s, find->arg);
+
+    return 0;
+}
+
+/*
+ * Loop through all dynamically created VMPortRpc devices and call
+ * func() for each instance.
+ */
+static int foreach_dynamic_VMPortRpc_device(FindVMPortRpcDeviceFunc *func,
+                                            void *arg)
+{
+    VMPortRpcFind find = {
+        .func = func,
+        .arg = arg,
+    };
+
+    /* Loop through all VMPortRpc devices that were spawened outside
+     * the machine */
+    find_VMPortRpc_device(qdev_get_machine(), &find);
+    if (find.found) {
+        return find.rc;
+    } else {
+        return VMPORT_DEVICE_NOT_FOUND;
+    }
+}
+
 #ifdef VMPORT_RPC_DEBUG
 /*
  * Add helper function for traceing.  This routine will convert
@@ -465,6 +515,26 @@ static int get_guestinfo(VMPortRpcState *s,
     return GUESTINFO_NOTFOUND;
 }
 
+static int get_qmp_guestinfo(VMPortRpcState *s,
+                             unsigned int a_key_len, char *a_info_key,
+                             unsigned int *a_value_len, void **a_value_data)
+{
+    unsigned int i;
+
+    for (i = 0; i < s->used_guestinfo; i++) {
+        if (s->guestinfo[i] &&
+            (s->guestinfo[i]->key_len == a_key_len) &&
+            (memcmp(a_info_key, s->guestinfo[i]->key_data,
+                    s->guestinfo[i]->key_len) == 0)) {
+            *a_value_len = s->guestinfo[i]->val_len;
+            *a_value_data = s->guestinfo[i]->val_data;
+            return 0;
+        }
+    }
+
+    return GUESTINFO_NOTFOUND;
+}
+
 static int set_guestinfo(VMPortRpcState *s, int a_key_len,
                          unsigned int a_val_len, char *a_info_key, char *val)
 {
@@ -864,6 +934,210 @@ static uint32_t vmport_rpc_ioport_read(void *opaque, 
uint32_t addr)
     return ur.data[0];
 }
 
+static int find_send(VMPortRpcState *s, void *arg)
+{
+    return ctrl_send(s, arg);
+}
+
+static void convert_local_rc(Error **errp, int rc)
+{
+    switch (rc) {
+    case 0:
+        break;
+    case VMPORT_DEVICE_NOT_FOUND:
+        error_set(errp, QERR_DEVICE_NOT_FOUND, TYPE_VMPORT_RPC);
+        break;
+    case SEND_NOT_OPEN:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc not open");
+        break;
+    case SEND_SKIPPED:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc send skipped");
+        break;
+    case SEND_TRUCATED:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc send trucated");
+        break;
+    case SEND_NO_MEMORY:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare rpc send out of memory");
+        break;
+    case GUESTINFO_NOTFOUND:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare guestinfo not found");
+        break;
+    case GUESTINFO_VALTOOLONG:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare guestinfo value too long");
+        break;
+    case GUESTINFO_KEYTOOLONG:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare guestinfo key too long");
+        break;
+    case GUESTINFO_TOOMANYKEYS:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare guestinfo too many keys");
+        break;
+    case GUESTINFO_NO_MEMORY:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare guestinfo out of memory");
+        break;
+    default:
+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                  "VMWare rpc send rc=%d unknown", rc);
+        break;
+    }
+}
+
+void qmp_inject_vmport_reboot(Error **errp)
+{
+    int rc = foreach_dynamic_VMPortRpc_device(find_send, (void *)"OS_Reboot");
+
+    convert_local_rc(errp, rc);
+}
+
+void qmp_inject_vmport_halt(Error **errp)
+{
+    int rc = foreach_dynamic_VMPortRpc_device(find_send, (void *)"OS_Halt");
+
+    convert_local_rc(errp, rc);
+}
+
+typedef struct keyValue {
+    void *key_data;
+    void *value_data;
+    unsigned int key_len;
+    unsigned int value_len;
+} keyValue;
+
+static int find_set(VMPortRpcState *s, void *arg)
+{
+    keyValue *key_value = arg;
+
+    return set_guestinfo(s, key_value->key_len, key_value->value_len,
+                         key_value->key_data, key_value->value_data);
+}
+
+static int find_get(VMPortRpcState *s, void *arg)
+{
+    keyValue *key_value = arg;
+
+    return get_qmp_guestinfo(s, key_value->key_len, key_value->key_data,
+                             &key_value->value_len, &key_value->value_data);
+}
+
+void qmp_vmport_guestinfo_set(const char *key, const char *value,
+                              bool has_format, enum DataFormat format,
+                              Error **errp)
+{
+    int rc;
+    keyValue key_value;
+
+    if (strncmp(key, "guestinfo.", strlen("guestinfo.")) == 0) {
+        key_value.key_data = (void *)(key + strlen("guestinfo."));
+        key_value.key_len = strlen(key) - strlen("guestinfo.");
+    } else {
+        key_value.key_data = (void *)key;
+        key_value.key_len = strlen(key);
+    }
+    if (has_format && (format == DATA_FORMAT_BASE64)) {
+        gsize write_count;
+
+        key_value.value_data = g_base64_decode(value, &write_count);
+        key_value.value_len = write_count;
+    } else {
+        key_value.value_data = (void *)value;
+        key_value.value_len = strlen(value);
+    }
+
+    rc = foreach_dynamic_VMPortRpc_device(find_set, (void *)&key_value);
+
+    if (key_value.value_data != value) {
+        g_free(key_value.value_data);
+    }
+
+    if (rc) {
+        convert_local_rc(errp, rc);
+        return;
+    }
+}
+
+char *qmp_vmport_guestinfo_get(const char *key,
+                               bool has_format, enum DataFormat format,
+                               Error **errp)
+{
+    int rc;
+    keyValue key_value;
+    char *value;
+
+    if (strncmp(key, "guestinfo.", strlen("guestinfo.")) == 0) {
+        key_value.key_data = (void *)(key + strlen("guestinfo."));
+        key_value.key_len = strlen(key) - strlen("guestinfo.");
+    } else {
+        key_value.key_data = (void *)key;
+        key_value.key_len = strlen(key);
+    }
+
+    rc = foreach_dynamic_VMPortRpc_device(find_get, (void *)&key_value);
+    if (rc) {
+        convert_local_rc(errp, rc);
+        return NULL;
+    }
+
+    if (has_format && (format == DATA_FORMAT_BASE64)) {
+        value = g_base64_encode(key_value.value_data, key_value.value_len);
+    } else {
+        /*
+         * FIXME should check for complete, valid UTF-8 characters.
+         * Invalid sequences should be replaced by a suitable
+         * replacement character.
+         */
+        value = g_malloc(key_value.value_len + 1);
+        memcpy(value, key_value.value_data, key_value.value_len);
+        value[key_value.value_len] = 0;
+    }
+
+    return value;
+}
+
+static int find_list(VMPortRpcState *s, void *arg)
+{
+    VmportGuestInfoList **keys = arg;
+    unsigned int i;
+
+    for (i = 0; i < s->used_guestinfo; i++) {
+        if (s->guestinfo[i] && s->guestinfo[i]->key_len) {
+            VmportGuestInfoList *info = g_malloc0(sizeof(*info));
+
+            info->value = g_malloc0(sizeof(*info->value));
+#ifdef VMPORT_SHORT
+            info->value->key = g_strndup(s->guestinfo[i]->key_data,
+                                         s->guestinfo[i]->key_len);
+#else
+            info->value->key = g_strdup_printf("guestinfo.%.*s",
+                                               (int)s->guestinfo[i]->key_len,
+                                               s->guestinfo[i]->key_data);
+#endif
+
+            info->next = *keys;
+            *keys = info;
+        }
+    }
+
+    return 0;
+}
+
+VmportGuestInfoList *qmp_query_vmport_guestinfo(Error **errp)
+{
+    VmportGuestInfoList *keys = NULL;
+    int rc = foreach_dynamic_VMPortRpc_device(find_list, (void *)&keys);
+
+    if (rc) {
+        convert_local_rc(errp, rc);
+    }
+
+    return keys;
+}
+
+
 static void vmport_rpc_reset(DeviceState *d)
 {
     VMPortRpcState *s = VMPORT_RPC(d);
@@ -871,6 +1145,7 @@ static void vmport_rpc_reset(DeviceState *d)
     s->reset_time = 14;
     s->build_number_value = 0;
     s->build_number_time = 0;
+    memset(&s->chans, 0, sizeof(s->chans));
 }
 
 static void vmport_rpc_realizefn(DeviceState *dev, Error **errp)
diff --git a/qapi-schema.json b/qapi-schema.json
index e16f8eb..6c3146f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1271,6 +1271,101 @@
 { 'command': 'inject-nmi' }
 
 ##
+# @inject-vmport-reboot:
+#
+# Injects a VMWare Tools reboot to the guest.
+#
+# Returns:  If successful, nothing
+#
+# Since:  2.3
+#
+##
+{ 'command': 'inject-vmport-reboot' }
+
+##
+# @inject-vmport-halt:
+#
+# Injects a VMWare Tools halt to the guest.
+#
+# Returns:  If successful, nothing
+#
+# Since:  2.3
+#
+##
+{ 'command': 'inject-vmport-halt' }
+
+##
+# @vmport-guestinfo-set:
+#
+# Set a VMWare Tools guestinfo key to a value
+#
+# @key: the key to set
+#
+# @value: The data to set the key to
+#
+# @format: #optional value encoding (default 'utf8').
+#          - base64: value must be base64 encoded text.  Its binary
+#            decoding gets set.
+#            Bug: invalid base64 is currently not rejected.
+#            Whitespace *is* invalid.
+#          - utf8: value's UTF-8 encoding is written
+#          - value itself is always Unicode regardless of format, like
+#            any other string.
+#
+# Returns: Nothing on success
+#
+# Since: 2.3
+##
+{ 'command': 'vmport-guestinfo-set',
+  'data': {'key': 'str', 'value': 'str',
+           '*format': 'DataFormat'} }
+
+##
+# @vmport-guestinfo-get:
+#
+# Get a VMWare Tools guestinfo value for a key
+#
+# @key: the key to get
+#
+# @format: #optional data encoding (default 'utf8').
+#          - base64: the value is returned in base64 encoding.
+#          - utf8: the value is interpreted as UTF-8.
+#            Bug: can screw up when the buffer contains invalid UTF-8
+#            sequences, NUL characters.
+#          - The return value is always Unicode regardless of format,
+#            like any other string.
+#
+# Returns: value for the guest info key
+#
+# Since: 2.3
+##
+{ 'command': 'vmport-guestinfo-get',
+  'data': {'key': 'str', '*format': 'DataFormat'},
+  'returns': 'str' }
+
+##
+# @VmportGuestInfo:
+#
+# Information about a single VMWare Tools guestinfo
+#
+# @key: The known key
+#
+# Since: 2.3
+##
+{ 'type': 'VmportGuestInfo', 'data': {'key': 'str'} }
+
+##
+# @query-vmport-guestinfo:
+#
+# Returns information about VMWare Tools guestinfo
+#
+# Returns: a list of @VmportGuestInfo
+#
+# Since: 2.3
+##
+{ 'command': 'query-vmport-guestinfo', 'returns': ['VmportGuestInfo'] }
+
+##
 # @set_link:
 #
 # Sets the link status of a virtual network adapter.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index c5f16dd..0aa11df 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -491,6 +491,147 @@ Note: inject-nmi fails when the guest doesn't support 
injecting.
 EQMP
 
     {
+        .name       = "inject-vmport-reboot",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_inject_vmport_reboot,
+    },
+
+SQMP
+inject-vmport-reboot
+----------
+
+Inject a VMWare Tools reboot to the guest.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "inject-vmport-reboot" }
+<- { "return": {} }
+
+Note: inject-vmport-reboot fails when the guest doesn't support injecting.
+
+EQMP
+
+    {
+        .name       = "inject-vmport-halt",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_inject_vmport_halt,
+    },
+
+SQMP
+inject-vmport-halt
+----------
+
+Inject a VMWare Tools halt to the guest.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "inject-vmport-halt" }
+<- { "return": {} }
+
+Note: inject-vmport-halt fails when the guest doesn't support injecting.
+
+EQMP
+
+    {
+        .name       = "vmport-guestinfo-set",
+        .args_type  = "key:s,value:s,format:s?",
+        .mhandler.cmd_new = qmp_marshal_input_vmport_guestinfo_set,
+    },
+
+SQMP
+vmport-guestinfo-set
+----------
+
+Set a VMWare Tools guestinfo key to a value
+
+Arguments:
+
+- "key": the key to set (json-string)
+- "value": data to write (json-string)
+- "format": data format (json-string, optional)
+          - Possible values: "utf8" (default), "base64"
+            Bug: invalid base64 is currently not rejected.
+            Whitespace *is* invalid.
+
+Example:
+
+-> { "execute": "vmport-guestinfo-set",
+                "arguments": { "key": "foo",
+                               "value": "abcdefgh",
+                               "format": "utf8" } }
+<- { "return": {} }
+
+EQMP
+
+    {
+        .name       = "vmport-guestinfo-get",
+        .args_type  = "key:s,format:s?",
+        .mhandler.cmd_new = qmp_marshal_input_vmport_guestinfo_get,
+    },
+
+SQMP
+vmport-guestinfo-get
+----------
+
+Get a VMWare Tools guestinfo value for a key
+
+Arguments:
+
+- "key": the key to get (json-string)
+- "format": data format (json-string, optional)
+          - Possible values: "utf8" (default), "base64"
+          - Naturally, format "utf8" works only when the ring buffer
+            contains valid UTF-8 text.  Invalid UTF-8 sequences get
+            replaced.  Bug: replacement doesn't work.  Bug: can screw
+            up on encountering NUL characters.
+
+Example:
+
+-> { "execute": "vmport-guestinfo-get",
+                "arguments": { "key": "foo",
+                               "format": "utf8" } }
+<- {"return": "abcdefgh"}
+
+
+EQMP
+
+    {
+        .name       = "query-vmport-guestinfo",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_vmport_guestinfo,
+    },
+
+SQMP
+query-vmport-guestinfo
+----------
+
+Returns information about VMWare Tools guestinfo.  The returned value is a 
json-array
+of all keys.
+
+Example:
+
+-> { "execute": "query-vmport-guestinfo" }
+<- {
+      "return": [
+         {
+            "key": "ip",
+         },
+         {
+            "key": "foo",
+         },
+         {
+            "key": "long",
+         }
+      ]
+   }
+
+EQMP
+
+    {
         .name       = "ringbuf-write",
         .args_type  = "device:s,data:s,format:s?",
         .mhandler.cmd_new = qmp_marshal_input_ringbuf_write,
-- 
1.8.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]