[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
- [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc, Don Slutz, 2015/01/30
- [Qemu-devel] [PATCH 2/6] vmport_rpc: Add the object vmport_rpc, Don Slutz, 2015/01/30
- [Qemu-devel] [PATCH 1/6] vmport.c: Fix vmport_cmd_ram_size, Don Slutz, 2015/01/30
- [Qemu-devel] [PATCH 5/6] vmport: Add VMware all ring hack, Don Slutz, 2015/01/30
- [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object.,
Don Slutz <=
- [Qemu-devel] [PATCH 3/6] vmport_rpc: Add limited support of VMware's hyper-call rpc, Don Slutz, 2015/01/30
- [Qemu-devel] [PATCH 6/6] MAINTAINERS: add VMware port, Don Slutz, 2015/01/30