qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH v7 6/9] vmport_rpc: Add QMP access to vmport_rpc obj


From: Don Slutz
Subject: [Qemu-devel] [PATCH v7 6/9] vmport_rpc: Add QMP access to vmport_rpc object.
Date: Fri, 12 Jun 2015 10:05:53 -0400

This adds one new inject command:

inject-vmport-action

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>
CC: Don Slutz <address@hidden>
---
v7:
  Delay call on g_strndup till after key length check.

    Why not assert(find) instead of leaving it to the comment?
      Switch to assert.
    Is it worth marking arg const here and in the VMPortRpcFind struct
      Switch to const.
    I'd rather abort() if someone compiled with -NDEBUG
      Done.
    Still mismatches on ---- line length (several sites).
      Done


 hw/misc/vmport_rpc.c | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 monitor.c            |  24 +++++
 qapi-schema.json     | 101 ++++++++++++++++++
 qmp-commands.hx      | 119 +++++++++++++++++++++
 4 files changed, 533 insertions(+), 2 deletions(-)

diff --git a/hw/misc/vmport_rpc.c b/hw/misc/vmport_rpc.c
index e2d5c36..e122bad 100644
--- a/hw/misc/vmport_rpc.c
+++ b/hw/misc/vmport_rpc.c
@@ -178,6 +178,19 @@ typedef struct VMPortRpcState {
 #endif
 } VMPortRpcState;
 
+typedef int FindVMPortRpcDeviceFunc(VMPortRpcState *s, const void *arg);
+
+/*
+ * The input and output argument that find_VMPortRpc_device() uses
+ * to do it's work.
+ */
+typedef struct VMPortRpcFind {
+    const void *arg;
+    FindVMPortRpcDeviceFunc *func;
+    int found;
+    int rc;
+} VMPortRpcFind;
+
 /* Basic request types */
 typedef enum {
     MESSAGE_TYPE_OPEN,
@@ -202,6 +215,56 @@ typedef struct {
     uint32_t edi;
 } vregs;
 
+/*
+ * Run func() for every VMPortRpc device, traverse the tree for
+ * everything else.
+ */
+static int find_VMPortRpc_device(Object *obj, void *opaque)
+{
+    VMPortRpcFind *find = opaque;
+    Object *dev;
+    VMPortRpcState *s;
+
+    assert(find);
+    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_vmport_rpc_device(FindVMPortRpcDeviceFunc *func,
+                                             const void *arg)
+{
+    VMPortRpcFind find = {
+        .func = func,
+        .arg = arg,
+    };
+
+    /* Loop through all VMPortRpc devices that were spawned 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 tracing.  This routine will convert
@@ -329,7 +392,7 @@ static int vmport_rpc_send(VMPortRpcState *s, channel_t *c,
     return rc;
 }
 
-static int vmport_rpc_ctrl_send(VMPortRpcState *s, char *msg)
+static int vmport_rpc_ctrl_send(VMPortRpcState *s, const char *msg)
 {
     int rc = SEND_NOT_OPEN;
     unsigned int i;
@@ -457,6 +520,29 @@ static int get_guestinfo(VMPortRpcState *s,
     return GUESTINFO_NOTFOUND;
 }
 
+static int get_qmp_guestinfo(VMPortRpcState *s,
+                             unsigned int a_key_len, const char *a_info_key,
+                             unsigned int *a_value_len, void ** a_value_data)
+{
+    guestinfo_t *gi = NULL;
+
+    if (a_key_len <= MAX_KEY_LEN) {
+        gpointer key = g_strndup(a_info_key, a_key_len);
+
+        gi = (guestinfo_t *)g_hash_table_lookup(s->guestinfo, key);
+        g_free(key);
+    } else {
+        return GUESTINFO_KEYTOOLONG;
+    }
+    if (gi) {
+        *a_value_len = gi->val_len;
+        *a_value_data = gi->val_data;
+        return 0;
+    }
+
+    return GUESTINFO_NOTFOUND;
+}
+
 static int set_guestinfo(VMPortRpcState *s, int a_key_len,
                          unsigned int a_val_len, const char *a_info_key,
                          char *val)
@@ -775,7 +861,7 @@ static void vmport_rpc(VMPortRpcState *s , vregs *ur)
     }
     if (delta > s->reset_time) {
         trace_vmport_rpc_ping(delta);
-        vmport_rpc_ctrl_send(s, (char *)"reset");
+        vmport_rpc_ctrl_send(s, "reset");
     }
     vmport_rpc_sweep(s, now_time);
     do {
@@ -848,6 +934,207 @@ static uint32_t vmport_rpc_ioport_read(void *opaque, 
uint32_t addr)
     return ur.data[0];
 }
 
+static int vmport_rpc_find_send(VMPortRpcState *s, const void *arg)
+{
+    return vmport_rpc_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_setg(errp, "VMWare rpc not open");
+        break;
+    case SEND_SKIPPED:
+        error_setg(errp, "VMWare rpc send skipped");
+        break;
+    case SEND_TRUCATED:
+        error_setg(errp, "VMWare rpc send trucated");
+        break;
+    case SEND_NO_MEMORY:
+        error_setg(errp, "VMWare rpc send out of memory");
+        break;
+    case GUESTINFO_NOTFOUND:
+        error_setg(errp, "VMWare guestinfo not found");
+        break;
+    case GUESTINFO_VALTOOLONG:
+        error_setg(errp, "VMWare guestinfo value too long");
+        break;
+    case GUESTINFO_KEYTOOLONG:
+        error_setg(errp, "VMWare guestinfo key too long");
+        break;
+    case GUESTINFO_TOOMANYKEYS:
+        error_setg(errp, "VMWare guestinfo too many keys");
+        break;
+    case GUESTINFO_NO_MEMORY:
+        error_setg(errp, "VMWare guestinfo out of memory");
+        break;
+    default:
+        error_setg(errp, "VMWare rpc send rc=%d unknown", rc);
+        break;
+    }
+}
+
+void qmp_inject_vmport_action(enum VmportAction action, Error **errp)
+{
+    int rc;
+
+    switch (action) {
+    case VMPORT_ACTION_REBOOT:
+        rc = foreach_dynamic_vmport_rpc_device(vmport_rpc_find_send,
+                                               "OS_Reboot");
+        break;
+    case VMPORT_ACTION_HALT:
+        rc = foreach_dynamic_vmport_rpc_device(vmport_rpc_find_send,
+                                               "OS_Halt");
+        break;
+    case VMPORT_ACTION_MAX:
+        assert(action != VMPORT_ACTION_MAX);
+        abort(); /* Should be impossible to get here. */
+        break;
+    }
+    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, const void *arg)
+{
+    const 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, const void *arg)
+{
+    keyValue *key_value = (void *)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_vmport_rpc_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;
+    }
+}
+
+VmportGuestInfoValue *qmp_vmport_guestinfo_get(const char *key,
+                                               bool has_format,
+                                               enum DataFormat format,
+                                               Error **errp)
+{
+    int rc;
+    keyValue key_value;
+    VmportGuestInfoValue *ret_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_vmport_rpc_device(find_get, (void *)&key_value);
+    if (rc) {
+        convert_local_rc(errp, rc);
+        return NULL;
+    }
+
+    ret_value = g_malloc(sizeof(VmportGuestInfoKey));
+    if (has_format && (format == DATA_FORMAT_BASE64)) {
+        ret_value->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.
+         */
+        ret_value->value = g_malloc(key_value.value_len + 1);
+        memcpy(ret_value->value, key_value.value_data, key_value.value_len);
+        ret_value->value[key_value.value_len] = 0;
+    }
+
+    return ret_value;
+}
+
+
+static void vmport_rpc_find_list_one(gpointer key, gpointer value,
+                                     gpointer opaque)
+{
+    VmportGuestInfoKeyList **keys = opaque;
+    VmportGuestInfoKeyList *info = g_malloc0(sizeof(*info));
+    char *ckey = key;
+
+    info->value = g_malloc0(sizeof(*info->value));
+    info->value->key = g_strdup_printf("guestinfo.%s", ckey);
+    info->next = *keys;
+    *keys = info;
+}
+
+static int vmport_rpc_find_list(VMPortRpcState *s, const void *arg)
+{
+    g_hash_table_foreach(s->guestinfo, vmport_rpc_find_list_one, 
(gpointer)arg);
+    return 0;
+}
+
+VmportGuestInfoKeyList *qmp_query_vmport_guestinfo(Error **errp)
+{
+    VmportGuestInfoKeyList *keys = NULL;
+    int rc = foreach_dynamic_vmport_rpc_device(vmport_rpc_find_list,
+                                               (void *)&keys);
+
+    if (rc) {
+        convert_local_rc(errp, rc);
+    }
+
+    return keys;
+}
+
+
 static void vmport_rpc_reset(DeviceState *d)
 {
     unsigned int i;
diff --git a/monitor.c b/monitor.c
index 9afee7b..50bc610 100644
--- a/monitor.c
+++ b/monitor.c
@@ -5367,4 +5367,28 @@ void qmp_rtc_reset_reinjection(Error **errp)
 {
     error_set(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
 }
+void qmp_inject_vmport_action(enum VmportAction action, Error **errp)
+{
+    error_set(errp, QERR_FEATURE_DISABLED, "inject-vmport-action");
+}
+void qmp_vmport_guestinfo_set(const char *key, const char *value,
+                              bool has_format, enum DataFormat format,
+                              Error **errp)
+{
+    error_set(errp, QERR_FEATURE_DISABLED, "vmport-guestinfo-set");
+}
+VmportGuestInfoValue *qmp_vmport_guestinfo_get(const char *key,
+                                               bool has_format,
+                                               enum DataFormat format,
+                                               Error **errp)
+{
+    error_set(errp, QERR_FEATURE_DISABLED, "vmport-guestinfo-get");
+    return NULL;
+}
+VmportGuestInfoKeyList *qmp_query_vmport_guestinfo(Error **errp)
+{
+    error_set(errp, QERR_FEATURE_DISABLED, "query-vmport-guestinfo");
+    return NULL;
+}
 #endif
+
diff --git a/qapi-schema.json b/qapi-schema.json
index 6e17a5c..6646f2b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1429,6 +1429,107 @@
 { 'command': 'inject-nmi' }
 
 ##
+# @VmportAction:
+#
+# An enumeration of actions that can be requested via vmport RPC.
+#
+# @reboot: Ask the guest via VMware tools to reboot
+#
+# @halt: Ask the guest via VMware tools to halt
+#
+# Since: 2.4
+##
+{ 'enum': 'VmportAction',
+  'data': [ 'reboot', 'halt' ] }
+
+##
+# @inject-vmport-action:
+#
+# Injects a VMWare Tools action to the guest.
+#
+# Returns:  If successful, nothing
+#
+# Since:  2.4
+#
+##
+{ 'command': 'inject-vmport-action',
+  'data': {'action': 'VmportAction'} }
+
+##
+# @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 is assumed to be base64 encoded text.  Its binary
+#            decoding gets set.
+#          - utf8: value's UTF-8 encoding is used to set.
+#
+# Returns: Nothing on success
+#
+# Since: 2.4
+##
+{ 'command': 'vmport-guestinfo-set',
+  'data': {'key': 'str', 'value': 'str',
+           '*format': 'DataFormat'} }
+
+##
+# @VmportGuestInfoValue:
+#
+# Information about a single VMWare Tools guestinfo
+#
+# @value: The value for a key
+#
+# Since: 2.4
+##
+{ 'struct': 'VmportGuestInfoValue', 'data': {'value': 'str'} }
+
+##
+# @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.
+#
+# Returns: value for the guest info key
+#
+# Since: 2.4
+##
+{ 'command': 'vmport-guestinfo-get',
+  'data': {'key': 'str', '*format': 'DataFormat'},
+  'returns': 'VmportGuestInfoValue' }
+
+##
+# @VmportGuestInfoKey:
+#
+# Information about a single VMWare Tools guestinfo
+#
+# @key: The known key
+#
+# Since: 2.4
+##
+{ 'struct': 'VmportGuestInfoKey', 'data': {'key': 'str'} }
+
+##
+# @query-vmport-guestinfo:
+#
+# Returns information about VMWare Tools guestinfo
+#
+# Returns: a list of @VmportGuestInfoKey
+#
+# Since: 2.4
+##
+{ 'command': 'query-vmport-guestinfo', 'returns': ['VmportGuestInfoKey'] }
+
+##
 # @set_link:
 #
 # Sets the link status of a virtual network adapter.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 867a21f..2035dcf 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -490,6 +490,125 @@ Note: inject-nmi fails when the guest doesn't support 
injecting.
 EQMP
 
     {
+        .name       = "inject-vmport-action",
+        .args_type  = "action:s",
+        .mhandler.cmd_new = qmp_marshal_input_inject_vmport_action,
+    },
+
+SQMP
+inject-vmport-action
+--------------------
+
+Inject a VMWare Tools action to the guest.
+
+Arguments:
+
+- "action": vmport action (json-string)
+          - Possible values: "reboot", "halt"
+
+Example:
+
+-> { "execute": "inject-vmport-action",
+                "arguments": { "action": "reboot" } }
+<- { "return": {} }
+
+Note: inject-vmport-action 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"
+
+Example:
+
+-> { "execute": "vmport-guestinfo-set",
+                "arguments": { "key": "guestinfo.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 value
+            contains valid UTF-8 text.  Bug: replacement doesn't work.
+           Bug: can screw up on encountering NUL characters.
+
+Example:
+
+-> { "execute": "vmport-guestinfo-get",
+                "arguments": { "key": "guestinfo.foo",
+                               "format": "utf8" } }
+<- {"return": {"value": "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": "guestinfo.ip"
+         },
+         {
+            "key": "guestinfo.foo"
+         },
+         {
+            "key": "guestinfo.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]