All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
From: Don Slutz <dslutz@verizon.com>
To: qemu-devel@nongnu.org
Cc: "Michael S. Tsirkin" <mst@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Don Slutz" <dslutz@verizon.com>,
	"Luiz Capitulino" <lcapitulino@redhat.com>,
	"Don Slutz" <don.slutz@gmail.com>,
	"Anthony Liguori" <aliguori@amazon.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Andreas Färber" <afaerber@suse.de>,
	"Richard Henderson" <rth@twiddle.net>
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	[thread overview]
Message-ID: <1434117956-4929-7-git-send-email-dslutz@verizon.com> (raw)
In-Reply-To: <1434117956-4929-1-git-send-email-dslutz@verizon.com>

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 <dslutz@verizon.com>
CC: Don Slutz <don.slutz@gmail.com>
---
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

  parent reply	other threads:[~2015-06-12 14:06 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-12 14:05 [Qemu-devel] [PATCH v7 0/9] Add limited support of VMware's hyper-call rpc Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [BUGFIX][PATCH v7 1/9] vmport: The io memory region needs to be at least a size of 4 Don Slutz
2015-06-12 22:38   ` Eric Blake
2015-06-15 13:53     ` Don Slutz
2015-06-15 15:09       ` Eric Blake
2015-06-15 16:15         ` Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [PATCH v7 2/9] vmport: Switch to trace Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [BUGFIX][PATCH v7 3/9] vmport: Fix vmport_cmd_ram_size Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [PATCH v7 4/9] vmport_rpc: Add the object vmport_rpc Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [PATCH v7 5/9] vmport_rpc: Add limited support of VMware's hyper-call rpc Don Slutz
2015-06-12 14:05 ` Don Slutz [this message]
2015-06-15 15:53   ` [Qemu-devel] [PATCH v7 6/9] vmport_rpc: Add QMP access to vmport_rpc object Eric Blake
2015-06-17 16:42     ` Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [PATCH v7 7/9] vmport_rpc: Add migration Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [PATCH v7 8/9] vmport: Add VMware all ring hack Don Slutz
2015-06-12 14:05 ` [Qemu-devel] [PATCH v7 9/9] MAINTAINERS: add VMware port Don Slutz
2015-06-17 13:44 ` [Qemu-devel] [PATCH v7 0/9] Add limited support of VMware's hyper-call rpc Paolo Bonzini
2015-06-17 22:26   ` Don Slutz
2015-06-18  7:58     ` Michael S. Tsirkin
2015-06-18  8:33       ` Paolo Bonzini
2015-06-18  9:40         ` Michael S. Tsirkin
2015-06-18  8:33     ` Paolo Bonzini
2015-06-17 14:11 ` Michael S. Tsirkin
2015-06-17 14:13   ` Paolo Bonzini
2015-06-17 14:18     ` Michael S. Tsirkin
2015-06-17 14:27       ` Paolo Bonzini
2015-06-17 14:29         ` Michael S. Tsirkin
2015-06-17 16:17           ` Paolo Bonzini
2015-06-17 16:29             ` Michael S. Tsirkin
2015-06-17 16:48               ` Paolo Bonzini
2015-06-17 18:43                 ` Michael S. Tsirkin
2015-06-17 19:15                   ` Paolo Bonzini
2015-06-17 19:22                     ` Michael S. Tsirkin
2015-06-17 19:24                       ` Paolo Bonzini
2015-06-17 19:29                         ` Michael S. Tsirkin
2015-06-17 17:03               ` Don Slutz
2015-06-17 17:14                 ` Paolo Bonzini
2015-06-17 17:25                   ` Paolo Bonzini
2015-06-17 17:34                     ` Don Slutz
2015-06-17 18:58                       ` Michael S. Tsirkin
2015-06-18 18:40                         ` Don Slutz
2015-06-17 18:45                   ` Michael S. Tsirkin
2015-06-17 18:45                 ` Michael S. Tsirkin
2015-06-18  7:03             ` Gerd Hoffmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1434117956-4929-7-git-send-email-dslutz@verizon.com \
    --to=dslutz@verizon.com \
    --cc=afaerber@suse.de \
    --cc=aliguori@amazon.com \
    --cc=armbru@redhat.com \
    --cc=don.slutz@gmail.com \
    --cc=lcapitulino@redhat.com \
    --cc=mst@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rth@twiddle.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.