[libvirt] [PATCH] Fix handling of stream EOF
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
Very occassionally the sequence of events from poll would result
in getting a HANGUP on its own, instead of a HANGUP+READABLE
at the same time. In the former case we would send back an error
event to the client, but never send the empty packet to indicate
EOF.
---
daemon/stream.c | 40 ++++++++++++++++++++++++++++++++++++----
1 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/daemon/stream.c b/daemon/stream.c
index 7df9952..50f8fd4 100644
--- a/daemon/stream.c
+++ b/daemon/stream.c
@@ -143,7 +143,8 @@ daemonStreamEvent(virStreamPtr st, int events, void *opaque)
VIR_DEBUG("st=%p events=%d EOF=%d closed=%d", st, events, stream->recvEOF, stream->closed);
- if (events & VIR_STREAM_EVENT_WRITABLE) {
+ if (!stream->closed &&
+ (events & VIR_STREAM_EVENT_WRITABLE)) {
if (daemonStreamHandleWrite(client, stream) < 0) {
daemonRemoveClientStream(client, stream);
virNetServerClientClose(client);
@@ -151,9 +152,9 @@ daemonStreamEvent(virStreamPtr st, int events, void *opaque)
}
}
- if (!stream->recvEOF &&
- (events & (VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_HANGUP))) {
- events = events & ~(VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_HANGUP);
+ if (!stream->closed && !stream->recvEOF &&
+ (events & (VIR_STREAM_EVENT_READABLE))) {
+ events = events & ~(VIR_STREAM_EVENT_READABLE);
if (daemonStreamHandleRead(client, stream) < 0) {
daemonRemoveClientStream(client, stream);
virNetServerClientClose(client);
@@ -190,6 +191,37 @@ daemonStreamEvent(virStreamPtr st, int events, void *opaque)
}
}
+
+ /* If we got HANGUP, we need to only send an empty
+ * packet so the client sees an EOF and cleans up
+ */
+ if (!stream->closed && !stream->recvEOF &&
+ (events & VIR_STREAM_EVENT_HANGUP)) {
+ virNetMessagePtr msg;
+ events &= ~(VIR_STREAM_EVENT_HANGUP);
+ stream->tx = 0;
+ stream->recvEOF = 1;
+ if (!(msg = virNetMessageNew(false))) {
+ daemonRemoveClientStream(client, stream);
+ virNetServerClientClose(client);
+ goto cleanup;
+ }
+ msg->cb = daemonStreamMessageFinished;
+ msg->opaque = stream;
+ stream->refs++;
+ if (virNetServerProgramSendStreamData(remoteProgram,
+ client,
+ msg,
+ stream->procedure,
+ stream->serial,
+ "", 0) < 0) {
+ virNetMessageFree(msg);
+ daemonRemoveClientStream(client, stream);
+ virNetServerClientClose(client);
+ goto cleanup;
+ }
+ }
+
if (!stream->closed &&
(events & (VIR_STREAM_EVENT_ERROR | VIR_STREAM_EVENT_HANGUP))) {
int ret;
--
1.7.6.4
13 years, 2 months
[libvirt] [PATCH] Fail gracefully when hashtables are NULL
by Marc-André Lureau
Instead of the message:
GLib-CRITICAL **: g_hash_table_get_values: assertion `hash_table !=
NULL' failed
---
libvirt-gobject/libvirt-gobject-connection.c | 18 ++++++++++++------
1 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/libvirt-gobject/libvirt-gobject-connection.c b/libvirt-gobject/libvirt-gobject-connection.c
index 95cd878..b7c0178 100644
--- a/libvirt-gobject/libvirt-gobject-connection.c
+++ b/libvirt-gobject/libvirt-gobject-connection.c
@@ -952,11 +952,15 @@ static void gvir_domain_ref(gpointer obj, gpointer ignore G_GNUC_UNUSED)
GList *gvir_connection_get_domains(GVirConnection *conn)
{
GVirConnectionPrivate *priv = conn->priv;
- GList *domains;
+ GList *domains = NULL;
+
g_mutex_lock(priv->lock);
- domains = g_hash_table_get_values(priv->domains);
- g_list_foreach(domains, gvir_domain_ref, NULL);
+ if (priv->domains != NULL) {
+ domains = g_hash_table_get_values(priv->domains);
+ g_list_foreach(domains, gvir_domain_ref, NULL);
+ }
g_mutex_unlock(priv->lock);
+
return domains;
}
@@ -969,11 +973,13 @@ GList *gvir_connection_get_domains(GVirConnection *conn)
GList *gvir_connection_get_storage_pools(GVirConnection *conn)
{
GVirConnectionPrivate *priv = conn->priv;
- GList *pools;
+ GList *pools = NULL;
g_mutex_lock(priv->lock);
- pools = g_hash_table_get_values(priv->pools);
- g_list_foreach(pools, gvir_domain_ref, NULL);
+ if (priv->pools != NULL) {
+ pools = g_hash_table_get_values(priv->pools);
+ g_list_foreach(pools, gvir_domain_ref, NULL);
+ }
g_mutex_unlock(priv->lock);
return pools;
--
1.7.6.2
13 years, 2 months
[libvirt] [PATCH] dissectors: Create dissector for Libvirt RPC
by Michal Privoznik
This patch creates basic dissector for Libvirt RPC. The protocol
description can be found here:
http://libvirt.org/internals/rpc.html
Currently, only packet head dissecting is written. To fully dissect
packet payloads a more effort is needed, as each function has
different arguments (in general). However, this can be good
stepping stone for later expansion. Ideally, a script that
will generate this dissector from libvirt RPC file would be written.
---
Okay, this patch obviously belongs to wireshark mailing list,
but before I'll send it there, I guess we should decide if we
want it there. I mean there are 2 modes/ways for wireshark
dissectors:
1) Place it into wireshark repo as many others.
Advantage: wireshark will be shipped with support for libvirt RPC
Disadvantage: wireshark will be shipped with support for libvirt RPC
In other words, if you look at wireshark releases, they are not
as often as ours, so in the end, this dissector will be always one
or more step behind current libvirt. But many users will be able
to use it right after box open.
2) Dissector as plugin
Advantage: we can update it as often as we want
Disadvantage: users needs to install a plugin
Personally, I prefer 2) as libvirt RPC is expanded pretty often,
and I expect this dissector to be used by libvirt developer mainly,
for who installing a plugin into wireshark can't be a real problem :)
The table of string for labeling procedures, has been generated by
hand, but I intend to write script for that.
Currently, only message header is dissected, no payload. Yeah, script
will generate structures for that too.
More on this:
http://www.wireshark.org/docs/wsdg_html_chunked/ChapterDissection.html
epan/CMakeLists.txt | 1 +
epan/dissectors/Makefile.common | 1 +
epan/dissectors/packet-libvirt.c | 466 ++++++++++++++++++++++++++++++++++++++
3 files changed, 468 insertions(+), 0 deletions(-)
create mode 100644 epan/dissectors/packet-libvirt.c
diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt
index 5d32e80..330cc9b 100644
--- a/epan/CMakeLists.txt
+++ b/epan/CMakeLists.txt
@@ -724,6 +724,7 @@ set(DISSECTOR_SRC
dissectors/packet-ldp.c
dissectors/packet-ldss.c
dissectors/packet-lge_monitor.c
+ dissectors/packet-libvirt.c
dissectors/packet-linx.c
dissectors/packet-lisp-data.c
dissectors/packet-lisp.c
diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common
index 080001a..c1d4043 100644
--- a/epan/dissectors/Makefile.common
+++ b/epan/dissectors/Makefile.common
@@ -644,6 +644,7 @@ DISSECTOR_SRC = \
packet-ldp.c \
packet-ldss.c \
packet-lge_monitor.c \
+ packet-libvirt.c \
packet-linx.c \
packet-lisp-data.c \
packet-lisp.c \
diff --git a/epan/dissectors/packet-libvirt.c b/epan/dissectors/packet-libvirt.c
new file mode 100644
index 0000000..f3735f0
--- /dev/null
+++ b/epan/dissectors/packet-libvirt.c
@@ -0,0 +1,466 @@
+/* packet-libvirt.c
+ * Routines for libvirt RPC packet disassembly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Author: Michal Privoznik <mprivozn(a)redhat.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include "packet-tcp.h"
+
+#define LIBVIRT_PORT 16509
+#define REMOTE_PROGRAM 0x20008086
+#define QEMU_PROGRAM 0x20008087
+
+static int proto_libvirt = -1;
+static int hf_libvirt_length = -1;
+static int hf_libvirt_program = -1;
+static int hf_libvirt_version = -1;
+static int hf_libvirt_procedure = -1;
+static int hf_libvirt_type = -1;
+static int hf_libvirt_serial = -1;
+static int hf_libvirt_status = -1;
+static int hf_qemu_procedure = -1;
+static gint ett_libvirt = -1;
+
+static const value_string program_strings[] = {
+ { REMOTE_PROGRAM, "REMOTE" },
+ { QEMU_PROGRAM, "QEMU" },
+ { 0, NULL }
+};
+
+static const value_string type_strings[] = {
+ { 0, "CALL" },
+ { 1, "REPLY" },
+ { 2, "MESSAGE" },
+ { 3, "STREAM" },
+ {-1, NULL}
+};
+
+static const value_string status_strings[] = {
+ { 0, "OK" },
+ { 1, "ERROR" },
+ { 2, "CONTINUE" },
+ {-1, NULL}
+};
+
+static const value_string remote_procedure_strings [] = {
+ { 1, "OPEN" },
+ { 2, "CLOSE" },
+ { 3, "GET_TYPE" },
+ { 4, "GET_VERSION" },
+ { 5, "GET_MAX_VCPUS" },
+ { 6, "NODE_GET_INFO" },
+ { 7, "GET_CAPABILITIES" },
+ { 8, "DOMAIN_ATTACH_DEVICE" },
+ { 9, "DOMAIN_CREATE" },
+ { 10, "DOMAIN_CREATE_XML" },
+ { 11, "DOMAIN_DEFINE_XML" },
+ { 12, "DOMAIN_DESTROY" },
+ { 13, "DOMAIN_DETACH_DEVICE" },
+ { 14, "DOMAIN_GET_XML_DESC" },
+ { 15, "DOMAIN_GET_AUTOSTART" },
+ { 16, "DOMAIN_GET_INFO" },
+ { 17, "DOMAIN_GET_MAX_MEMORY" },
+ { 18, "DOMAIN_GET_MAX_VCPUS" },
+ { 19, "DOMAIN_GET_OS_TYPE" },
+ { 20, "DOMAIN_GET_VCPUS" },
+ { 21, "LIST_DEFINED_DOMAINS" },
+ { 22, "DOMAIN_LOOKUP_BY_ID" },
+ { 23, "DOMAIN_LOOKUP_BY_NAME" },
+ { 24, "DOMAIN_LOOKUP_BY_UUID" },
+ { 25, "NUM_OF_DEFINED_DOMAINS" },
+ { 26, "DOMAIN_PIN_VCPU" },
+ { 27, "DOMAIN_REBOOT" },
+ { 28, "DOMAIN_RESUME" },
+ { 29, "DOMAIN_SET_AUTOSTART" },
+ { 30, "DOMAIN_SET_MAX_MEMORY" },
+ { 31, "DOMAIN_SET_MEMORY" },
+ { 32, "DOMAIN_SET_VCPUS" },
+ { 33, "DOMAIN_SHUTDOWN" },
+ { 34, "DOMAIN_SUSPEND" },
+ { 35, "DOMAIN_UNDEFINE" },
+ { 36, "LIST_DEFINED_NETWORKS" },
+ { 37, "LIST_DOMAINS" },
+ { 38, "LIST_NETWORKS" },
+ { 39, "NETWORK_CREATE" },
+ { 40, "NETWORK_CREATE_XML" },
+ { 41, "NETWORK_DEFINE_XML" },
+ { 42, "NETWORK_DESTROY" },
+ { 43, "NETWORK_GET_XML_DESC" },
+ { 44, "NETWORK_GET_AUTOSTART" },
+ { 45, "NETWORK_GET_BRIDGE_NAME" },
+ { 46, "NETWORK_LOOKUP_BY_NAME" },
+ { 47, "NETWORK_LOOKUP_BY_UUID" },
+ { 48, "NETWORK_SET_AUTOSTART" },
+ { 49, "NETWORK_UNDEFINE" },
+ { 50, "NUM_OF_DEFINED_NETWORKS" },
+ { 51, "NUM_OF_DOMAINS" },
+ { 52, "NUM_OF_NETWORKS" },
+ { 53, "DOMAIN_CORE_DUMP" },
+ { 54, "DOMAIN_RESTORE" },
+ { 55, "DOMAIN_SAVE" },
+ { 56, "DOMAIN_GET_SCHEDULER_TYPE" },
+ { 57, "DOMAIN_GET_SCHEDULER_PARAMETERS" },
+ { 58, "DOMAIN_SET_SCHEDULER_PARAMETERS" },
+ { 59, "GET_HOSTNAME" },
+ { 60, "SUPPORTS_FEATURE" },
+ { 61, "DOMAIN_MIGRATE_PREPARE" },
+ { 62, "DOMAIN_MIGRATE_PERFORM" },
+ { 63, "DOMAIN_MIGRATE_FINISH" },
+ { 64, "DOMAIN_BLOCK_STATS" },
+ { 65, "DOMAIN_INTERFACE_STATS" },
+ { 66, "AUTH_LIST" },
+ { 67, "AUTH_SASL_INIT" },
+ { 68, "AUTH_SASL_START" },
+ { 69, "AUTH_SASL_STEP" },
+ { 70, "AUTH_POLKIT" },
+ { 71, "NUM_OF_STORAGE_POOLS" },
+ { 72, "LIST_STORAGE_POOLS" },
+ { 73, "NUM_OF_DEFINED_STORAGE_POOLS" },
+ { 74, "LIST_DEFINED_STORAGE_POOLS" },
+ { 75, "FIND_STORAGE_POOL_SOURCES" },
+ { 76, "STORAGE_POOL_CREATE_XML" },
+ { 77, "STORAGE_POOL_DEFINE_XML" },
+ { 78, "STORAGE_POOL_CREATE" },
+ { 79, "STORAGE_POOL_BUILD" },
+ { 80, "STORAGE_POOL_DESTROY" },
+ { 81, "STORAGE_POOL_DELETE" },
+ { 82, "STORAGE_POOL_UNDEFINE" },
+ { 83, "STORAGE_POOL_REFRESH" },
+ { 84, "STORAGE_POOL_LOOKUP_BY_NAME" },
+ { 85, "STORAGE_POOL_LOOKUP_BY_UUID" },
+ { 86, "STORAGE_POOL_LOOKUP_BY_VOLUME" },
+ { 87, "STORAGE_POOL_GET_INFO" },
+ { 88, "STORAGE_POOL_GET_XML_DESC" },
+ { 89, "STORAGE_POOL_GET_AUTOSTART" },
+ { 90, "STORAGE_POOL_SET_AUTOSTART" },
+ { 91, "STORAGE_POOL_NUM_OF_VOLUMES" },
+ { 92, "STORAGE_POOL_LIST_VOLUMES" },
+ { 93, "STORAGE_VOL_CREATE_XML" },
+ { 94, "STORAGE_VOL_DELETE" },
+ { 95, "STORAGE_VOL_LOOKUP_BY_NAME" },
+ { 96, "STORAGE_VOL_LOOKUP_BY_KEY" },
+ { 97, "STORAGE_VOL_LOOKUP_BY_PATH" },
+ { 98, "STORAGE_VOL_GET_INFO" },
+ { 99, "STORAGE_VOL_GET_XML_DESC" },
+ { 100, "STORAGE_VOL_GET_PATH" },
+ { 101, "NODE_GET_CELLS_FREE_MEMORY" },
+ { 102, "NODE_GET_FREE_MEMORY" },
+ { 103, "DOMAIN_BLOCK_PEEK" },
+ { 104, "DOMAIN_MEMORY_PEEK" },
+ { 105, "DOMAIN_EVENTS_REGISTER" },
+ { 106, "DOMAIN_EVENTS_DEREGISTER" },
+ { 107, "DOMAIN_EVENT_LIFECYCLE" },
+ { 108, "DOMAIN_MIGRATE_PREPARE2" },
+ { 109, "DOMAIN_MIGRATE_FINISH2" },
+ { 110, "GET_URI" },
+ { 111, "NODE_NUM_OF_DEVICES" },
+ { 112, "NODE_LIST_DEVICES" },
+ { 113, "NODE_DEVICE_LOOKUP_BY_NAME" },
+ { 114, "NODE_DEVICE_GET_XML_DESC" },
+ { 115, "NODE_DEVICE_GET_PARENT" },
+ { 116, "NODE_DEVICE_NUM_OF_CAPS" },
+ { 117, "NODE_DEVICE_LIST_CAPS" },
+ { 118, "NODE_DEVICE_DETTACH" },
+ { 119, "NODE_DEVICE_RE_ATTACH" },
+ { 120, "NODE_DEVICE_RESET" },
+ { 121, "DOMAIN_GET_SECURITY_LABEL" },
+ { 122, "NODE_GET_SECURITY_MODEL" },
+ { 123, "NODE_DEVICE_CREATE_XML" },
+ { 124, "NODE_DEVICE_DESTROY" },
+ { 125, "STORAGE_VOL_CREATE_XML_FROM" },
+ { 126, "NUM_OF_INTERFACES" },
+ { 127, "LIST_INTERFACES" },
+ { 128, "INTERFACE_LOOKUP_BY_NAME" },
+ { 129, "INTERFACE_LOOKUP_BY_MAC_STRING" },
+ { 130, "INTERFACE_GET_XML_DESC" },
+ { 131, "INTERFACE_DEFINE_XML" },
+ { 132, "INTERFACE_UNDEFINE" },
+ { 133, "INTERFACE_CREATE" },
+ { 134, "INTERFACE_DESTROY" },
+ { 135, "DOMAIN_XML_FROM_NATIVE" },
+ { 136, "DOMAIN_XML_TO_NATIVE" },
+ { 137, "NUM_OF_DEFINED_INTERFACES" },
+ { 138, "LIST_DEFINED_INTERFACES" },
+ { 139, "NUM_OF_SECRETS" },
+ { 140, "LIST_SECRETS" },
+ { 141, "SECRET_LOOKUP_BY_UUID" },
+ { 142, "SECRET_DEFINE_XML" },
+ { 143, "SECRET_GET_XML_DESC" },
+ { 144, "SECRET_SET_VALUE" },
+ { 145, "SECRET_GET_VALUE" },
+ { 146, "SECRET_UNDEFINE" },
+ { 147, "SECRET_LOOKUP_BY_USAGE" },
+ { 148, "DOMAIN_MIGRATE_PREPARE_TUNNEL" },
+ { 149, "IS_SECURE" },
+ { 150, "DOMAIN_IS_ACTIVE" },
+ { 151, "DOMAIN_IS_PERSISTENT" },
+ { 152, "NETWORK_IS_ACTIVE" },
+ { 153, "NETWORK_IS_PERSISTENT" },
+ { 154, "STORAGE_POOL_IS_ACTIVE" },
+ { 155, "STORAGE_POOL_IS_PERSISTENT" },
+ { 156, "INTERFACE_IS_ACTIVE" },
+ { 157, "GET_LIB_VERSION" },
+ { 158, "CPU_COMPARE" },
+ { 159, "DOMAIN_MEMORY_STATS" },
+ { 160, "DOMAIN_ATTACH_DEVICE_FLAGS" },
+ { 161, "DOMAIN_DETACH_DEVICE_FLAGS" },
+ { 162, "CPU_BASELINE" },
+ { 163, "DOMAIN_GET_JOB_INFO" },
+ { 164, "DOMAIN_ABORT_JOB" },
+ { 165, "STORAGE_VOL_WIPE" },
+ { 166, "DOMAIN_MIGRATE_SET_MAX_DOWNTIME" },
+ { 167, "DOMAIN_EVENTS_REGISTER_ANY" },
+ { 168, "DOMAIN_EVENTS_DEREGISTER_ANY" },
+ { 169, "DOMAIN_EVENT_REBOOT" },
+ { 170, "DOMAIN_EVENT_RTC_CHANGE" },
+ { 171, "DOMAIN_EVENT_WATCHDOG" },
+ { 172, "DOMAIN_EVENT_IO_ERROR" },
+ { 173, "DOMAIN_EVENT_GRAPHICS" },
+ { 174, "DOMAIN_UPDATE_DEVICE_FLAGS" },
+ { 175, "NWFILTER_LOOKUP_BY_NAME" },
+ { 176, "NWFILTER_LOOKUP_BY_UUID" },
+ { 177, "NWFILTER_GET_XML_DESC" },
+ { 178, "NUM_OF_NWFILTERS" },
+ { 179, "LIST_NWFILTERS" },
+ { 180, "NWFILTER_DEFINE_XML" },
+ { 181, "NWFILTER_UNDEFINE" },
+ { 182, "DOMAIN_MANAGED_SAVE" },
+ { 183, "DOMAIN_HAS_MANAGED_SAVE_IMAGE" },
+ { 184, "DOMAIN_MANAGED_SAVE_REMOVE" },
+ { 185, "DOMAIN_SNAPSHOT_CREATE_XML" },
+ { 186, "DOMAIN_SNAPSHOT_GET_XML_DESC" },
+ { 187, "DOMAIN_SNAPSHOT_NUM" },
+ { 188, "DOMAIN_SNAPSHOT_LIST_NAMES" },
+ { 189, "DOMAIN_SNAPSHOT_LOOKUP_BY_NAME" },
+ { 190, "DOMAIN_HAS_CURRENT_SNAPSHOT" },
+ { 191, "DOMAIN_SNAPSHOT_CURRENT" },
+ { 192, "DOMAIN_REVERT_TO_SNAPSHOT" },
+ { 193, "DOMAIN_SNAPSHOT_DELETE" },
+ { 194, "DOMAIN_GET_BLOCK_INFO" },
+ { 195, "DOMAIN_EVENT_IO_ERROR_REASON" },
+ { 196, "DOMAIN_CREATE_WITH_FLAGS" },
+ { 197, "DOMAIN_SET_MEMORY_PARAMETERS" },
+ { 198, "DOMAIN_GET_MEMORY_PARAMETERS" },
+ { 199, "DOMAIN_SET_VCPUS_FLAGS" },
+ { 200, "DOMAIN_GET_VCPUS_FLAGS" },
+ { 201, "DOMAIN_OPEN_CONSOLE" },
+ { 202, "DOMAIN_IS_UPDATED" },
+ { 203, "GET_SYSINFO" },
+ { 204, "DOMAIN_SET_MEMORY_FLAGS" },
+ { 205, "DOMAIN_SET_BLKIO_PARAMETERS" },
+ { 206, "DOMAIN_GET_BLKIO_PARAMETERS" },
+ { 207, "DOMAIN_MIGRATE_SET_MAX_SPEED" },
+ { 208, "STORAGE_VOL_UPLOAD" },
+ { 209, "STORAGE_VOL_DOWNLOAD" },
+ { 210, "DOMAIN_INJECT_NMI" },
+ { 211, "DOMAIN_SCREENSHOT" },
+ { 212, "DOMAIN_GET_STATE" },
+ { 213, "DOMAIN_MIGRATE_BEGIN3" },
+ { 214, "DOMAIN_MIGRATE_PREPARE3" },
+ { 215, "DOMAIN_MIGRATE_PREPARE_TUNNEL3" },
+ { 216, "DOMAIN_MIGRATE_PERFORM3" },
+ { 217, "DOMAIN_MIGRATE_FINISH3" },
+ { 218, "DOMAIN_MIGRATE_CONFIRM3" },
+ { 219, "DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS" },
+ { 220, "INTERFACE_CHANGE_BEGIN" },
+ { 221, "INTERFACE_CHANGE_COMMIT" },
+ { 222, "INTERFACE_CHANGE_ROLLBACK" },
+ { 223, "DOMAIN_GET_SCHEDULER_PARAMETERS_FLAGS" },
+ { 224, "DOMAIN_EVENT_CONTROL_ERROR" },
+ { 225, "DOMAIN_PIN_VCPU_FLAGS" },
+ { 226, "DOMAIN_SEND_KEY" },
+ { 227, "NODE_GET_CPU_STATS" },
+ { 228, "NODE_GET_MEMORY_STATS" },
+ { 229, "DOMAIN_GET_CONTROL_INFO" },
+ { 230, "DOMAIN_GET_VCPU_PIN_INFO" },
+ { 231, "DOMAIN_UNDEFINE_FLAGS" },
+ { 232, "DOMAIN_SAVE_FLAGS" },
+ { 233, "DOMAIN_RESTORE_FLAGS" },
+ { 234, "DOMAIN_DESTROY_FLAGS" },
+ { 235, "DOMAIN_SAVE_IMAGE_GET_XML_DESC" },
+ { 236, "DOMAIN_SAVE_IMAGE_DEFINE_XML" },
+ { 237, "DOMAIN_BLOCK_JOB_ABORT" },
+ { 238, "DOMAIN_GET_BLOCK_JOB_INFO" },
+ { 239, "DOMAIN_BLOCK_JOB_SET_SPEED" },
+ { 240, "DOMAIN_BLOCK_PULL" },
+ { 241, "DOMAIN_EVENT_BLOCK_JOB" },
+ { 242, "DOMAIN_MIGRATE_GET_MAX_SPEED" },
+ { 243, "DOMAIN_BLOCK_STATS_FLAGS" },
+ { 244, "DOMAIN_SNAPSHOT_GET_PARENT" },
+ { 245, "DOMAIN_RESET" },
+ { 0, NULL}
+};
+
+static const value_string qemu_procedure_strings[] = {
+ { 1, "MONITOR_COMMAND" },
+ { 2, "DOMAIN_ATTACH" },
+ { 0, NULL}
+};
+
+static void
+dissect_libvirt_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ gint offset = 0;
+ /* Okay, these are magic constants, but
+ * they are just offsets where requested
+ * info is to be found */
+ guint32 prog = tvb_get_ntohl(tvb, 4);
+ guint32 proc = tvb_get_ntohl(tvb, 12);
+ guint32 type = tvb_get_ntohl(tvb, 16);
+ guint32 serial = tvb_get_ntohl(tvb, 20);
+ guint32 status = tvb_get_ntohl(tvb, 24);
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "Libvirt");
+ col_clear(pinfo->cinfo,COL_INFO);
+ if (prog == REMOTE_PROGRAM)
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Proc=%s ", val_to_str(proc, remote_procedure_strings, "%d"));
+ else if (prog == QEMU_PROGRAM)
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Proc=%s ", val_to_str(proc, qemu_procedure_strings, "%d"));
+ else
+ /* unhandeld program */
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Proc=%u ", proc);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "Type=%s Status=%s Prog=%s Serial=%u",
+ val_to_str(type, type_strings, "%d"),
+ val_to_str(status, status_strings, "%d"),
+ val_to_str(prog, program_strings, "%x"),
+ serial);
+
+ if (tree) {
+ proto_item *ti = NULL;
+ proto_tree *libvirt_tree = NULL;
+
+ ti = proto_tree_add_item(tree, proto_libvirt, tvb, 0, -1, FALSE);
+ libvirt_tree = proto_item_add_subtree(ti, ett_libvirt);
+ proto_tree_add_item(libvirt_tree, hf_libvirt_length, tvb, offset, 4, FALSE); offset += 4;
+ proto_tree_add_item(libvirt_tree, hf_libvirt_program, tvb, offset, 4, FALSE); offset += 4;
+ proto_tree_add_item(libvirt_tree, hf_libvirt_version, tvb, offset, 4, FALSE); offset += 4;
+ if (prog == REMOTE_PROGRAM)
+ proto_tree_add_item(libvirt_tree, hf_libvirt_procedure, tvb, offset, 4, FALSE);
+ else if (prog == QEMU_PROGRAM)
+ proto_tree_add_item(libvirt_tree, hf_qemu_procedure, tvb, offset, 4, FALSE);
+ else
+ /* unhandeld program */
+ proto_tree_add_none_format(libvirt_tree, -1, tvb, offset, 4, "Unknown proc: %u", proc);
+ offset += 4;
+ proto_tree_add_item(libvirt_tree, hf_libvirt_type, tvb, offset, 4, FALSE); offset += 4;
+ proto_tree_add_item(libvirt_tree, hf_libvirt_serial, tvb, offset, 4, FALSE); offset += 4;
+ proto_tree_add_item(libvirt_tree, hf_libvirt_status, tvb, offset, 4, FALSE); offset += 4;
+ }
+}
+
+static guint32 get_message_len(packet_info *pinfo __attribute__((unused)), tvbuff_t *tvb, int offset)
+{
+ return tvb_get_ntohl(tvb, offset);
+}
+
+static void
+dissect_libvirt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ /* Another magic const - 4; simply, how much bytes
+ * is needed to tell the length of libvirt packet. */
+ tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_message_len, dissect_libvirt_message);
+}
+
+
+void
+proto_register_libvirt(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_libvirt_length,
+ { "length", "libvirt.length",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_libvirt_program,
+ { "program", "libvirt.program",
+ FT_UINT32, BASE_HEX,
+ VALS(program_strings), 0x0,
+ NULL, HFILL}
+ },
+ { &hf_libvirt_version,
+ { "version", "libvirt.version",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_libvirt_procedure,
+ { "procedure", "libvirt.procedure",
+ FT_INT32, BASE_DEC,
+ VALS(remote_procedure_strings), 0x0,
+ NULL, HFILL}
+ },
+ { &hf_libvirt_type,
+ { "type", "libvirt.type",
+ FT_INT32, BASE_DEC,
+ VALS(type_strings), 0x0,
+ NULL, HFILL}
+ },
+ { &hf_libvirt_serial,
+ { "serial", "libvirt.serial",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_libvirt_status,
+ { "status", "libvirt.status",
+ FT_INT32, BASE_DEC,
+ VALS(status_strings), 0x0,
+ NULL, HFILL}
+ },
+ { &hf_qemu_procedure,
+ { "procedure", "libvirt.procedure",
+ FT_INT32, BASE_DEC,
+ VALS(qemu_procedure_strings), 0x0,
+ NULL, HFILL}
+ }
+ };
+
+ static gint *ett[] = {
+ &ett_libvirt
+ };
+
+ proto_libvirt = proto_register_protocol (
+ "Libvirt", /* name */
+ "libvirt", /* short name */
+ "libvirt" /* abbrev */
+ );
+
+ proto_register_field_array(proto_libvirt, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_foo(void)
+{
+ static dissector_handle_t libvirt_handle;
+
+ libvirt_handle = create_dissector_handle(dissect_libvirt, proto_libvirt);
+ dissector_add_uint("tcp.port", LIBVIRT_PORT, libvirt_handle);
+}
--
1.7.3.4
13 years, 2 months
[libvirt] [PATCH] Introduce <driver> under <filesystem> to support open-by-handle.
by Harsh Prateek Bora
VirtFS allows the user to choose between local/handle for fs driver.
As of now, libvirt hardcode to use local driver only. This patch provides a
solution to allow user to choose between local/handle as fs driver.
Sample:
<filesystem type='mount'>
<driver type='handle'/>
<source dir='/folder/to/share'/>
<target dir='mount_tag'/>
</filesystem>
Signed-off-by: Harsh Prateek Bora <harsh(a)linux.vnet.ibm.com>
---
docs/schemas/domaincommon.rng | 9 +++++++++
src/conf/domain_conf.c | 23 +++++++++++++++++++++++
src/conf/domain_conf.h | 10 ++++++++++
src/qemu/qemu_command.c | 7 ++++++-
4 files changed, 48 insertions(+), 1 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 492a41d..3a0cb86 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1025,6 +1025,15 @@
</choice>
<optional>
<ref name="address"/>
+ <element name="driver">
+ <attribute name="type">
+ <choice>
+ <value>local</value>
+ <value>handle</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
<attribute name="accessmode">
<choice>
<value>passthrough</value>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index a537251..04410f2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -238,6 +238,10 @@ VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
"file",
"template")
+VIR_ENUM_IMPL(virDomainFSDriverType, VIR_DOMAIN_FS_DRIVER_TYPE_LAST,
+ "local",
+ "handle")
+
VIR_ENUM_IMPL(virDomainFSAccessMode, VIR_DOMAIN_FS_ACCESSMODE_LAST,
"passthrough",
"mapped",
@@ -2828,6 +2832,7 @@ virDomainFSDefParseXML(xmlNodePtr node,
virDomainFSDefPtr def;
xmlNodePtr cur;
char *type = NULL;
+ char *fsdriver = NULL;
char *source = NULL;
char *target = NULL;
char *accessmode = NULL;
@@ -2878,11 +2883,23 @@ virDomainFSDefParseXML(xmlNodePtr node,
target = virXMLPropString(cur, "dir");
} else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
def->readonly = 1;
+ } else if ((fsdriver == NULL) && (xmlStrEqual(cur->name, BAD_CAST "driver"))) {
+ fsdriver = virXMLPropString(cur, "type");
}
}
cur = cur->next;
}
+ if (fsdriver) {
+ if ((def->fsdriver = virDomainFSDriverTypeTypeFromString(fsdriver)) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown fs driver type '%s'"), fsdriver);
+ goto error;
+ }
+ } else {
+ def->fsdriver = VIR_DOMAIN_FS_DRIVER_TYPE_LOCAL;
+ }
+
if (source == NULL) {
virDomainReportError(VIR_ERR_NO_SOURCE,
target ? "%s" : NULL, target);
@@ -2905,6 +2922,7 @@ virDomainFSDefParseXML(xmlNodePtr node,
cleanup:
VIR_FREE(type);
+ VIR_FREE(fsdriver);
VIR_FREE(target);
VIR_FREE(source);
VIR_FREE(accessmode);
@@ -9351,6 +9369,7 @@ virDomainFSDefFormat(virBufferPtr buf,
{
const char *type = virDomainFSTypeToString(def->type);
const char *accessmode = virDomainFSAccessModeTypeToString(def->accessmode);
+ const char *fsdriver = virDomainFSDriverTypeTypeToString(def->fsdriver);
if (!type) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -9369,6 +9388,10 @@ virDomainFSDefFormat(virBufferPtr buf,
" <filesystem type='%s' accessmode='%s'>\n",
type, accessmode);
+ if (def->fsdriver) {
+ virBufferAsprintf(buf, " <driver type='%s'/>\n", fsdriver);
+ }
+
if (def->src) {
switch (def->type) {
case VIR_DOMAIN_FS_TYPE_MOUNT:
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index e07fd2f..d42325e 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -368,6 +368,14 @@ enum virDomainFSType {
VIR_DOMAIN_FS_TYPE_LAST
};
+/* Filesystem driver type */
+enum virDomainFSDriverType {
+ VIR_DOMAIN_FS_DRIVER_TYPE_LOCAL,
+ VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE,
+
+ VIR_DOMAIN_FS_DRIVER_TYPE_LAST
+};
+
/* Filesystem mount access mode */
enum virDomainFSAccessMode {
VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH,
@@ -381,6 +389,7 @@ typedef struct _virDomainFSDef virDomainFSDef;
typedef virDomainFSDef *virDomainFSDefPtr;
struct _virDomainFSDef {
int type;
+ int fsdriver;
int accessmode;
char *src;
char *dst;
@@ -1856,6 +1865,7 @@ VIR_ENUM_DECL(virDomainController)
VIR_ENUM_DECL(virDomainControllerModelSCSI)
VIR_ENUM_DECL(virDomainControllerModelUSB)
VIR_ENUM_DECL(virDomainFS)
+VIR_ENUM_DECL(virDomainFSDriverType)
VIR_ENUM_DECL(virDomainFSAccessMode)
VIR_ENUM_DECL(virDomainNet)
VIR_ENUM_DECL(virDomainNetBackend)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index cf99f89..a989aa7 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1818,7 +1818,12 @@ char *qemuBuildFSStr(virDomainFSDefPtr fs,
goto error;
}
- virBufferAddLit(&opt, "local");
+ if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_LOCAL) {
+ virBufferAddLit(&opt, "local");
+ } else if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE) {
+ virBufferAddLit(&opt, "handle");
+ }
+
if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) {
virBufferAddLit(&opt, ",security_model=mapped");
} else if(fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) {
--
1.7.1.1
13 years, 2 months
[libvirt] [PATCH v2] Introduce <driver> under <filesystem> to support open-by-handle.
by Harsh Prateek Bora
VirtFS allows the user to choose between path/handle based fs driver.
As of now, libvirt hardcode to use path based driver only. This patch provides
a solution to allow user to choose between path/handle based fs driver.
Sample:
<filesystem type='mount'>
<driver type='handle'/>
<source dir='/folder/to/share1'/>
<target dir='mount_tag1'/>
</filesystem>
<filesystem type='mount'>
<driver type='path'/>
<source dir='/folder/to/share2'/>
<target dir='mount_tag2'/>
</filesystem>
Signed-off-by: Harsh Prateek Bora <harsh(a)linux.vnet.ibm.com>
v2:
- use 'path' instead of 'local' in xml terminology
- added default enum for optional driver type
---
docs/schemas/domaincommon.rng | 9 +++++++++
src/conf/domain_conf.c | 24 ++++++++++++++++++++++++
src/conf/domain_conf.h | 11 +++++++++++
src/qemu/qemu_command.c | 7 ++++++-
4 files changed, 50 insertions(+), 1 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 492a41d..3937393 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1025,6 +1025,15 @@
</choice>
<optional>
<ref name="address"/>
+ <element name="driver">
+ <attribute name="type">
+ <choice>
+ <value>path</value>
+ <value>handle</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
<attribute name="accessmode">
<choice>
<value>passthrough</value>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index a537251..ede1f01 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -238,6 +238,11 @@ VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
"file",
"template")
+VIR_ENUM_IMPL(virDomainFSDriverType, VIR_DOMAIN_FS_DRIVER_TYPE_LAST,
+ "default",
+ "path",
+ "handle")
+
VIR_ENUM_IMPL(virDomainFSAccessMode, VIR_DOMAIN_FS_ACCESSMODE_LAST,
"passthrough",
"mapped",
@@ -2828,6 +2833,7 @@ virDomainFSDefParseXML(xmlNodePtr node,
virDomainFSDefPtr def;
xmlNodePtr cur;
char *type = NULL;
+ char *fsdriver = NULL;
char *source = NULL;
char *target = NULL;
char *accessmode = NULL;
@@ -2878,11 +2884,23 @@ virDomainFSDefParseXML(xmlNodePtr node,
target = virXMLPropString(cur, "dir");
} else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
def->readonly = 1;
+ } else if ((fsdriver == NULL) && (xmlStrEqual(cur->name, BAD_CAST "driver"))) {
+ fsdriver = virXMLPropString(cur, "type");
}
}
cur = cur->next;
}
+ if (fsdriver) {
+ if ((def->fsdriver = virDomainFSDriverTypeTypeFromString(fsdriver)) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown fs driver type '%s'"), fsdriver);
+ goto error;
+ }
+ } else {
+ def->fsdriver = VIR_DOMAIN_FS_DRIVER_TYPE_PATH;
+ }
+
if (source == NULL) {
virDomainReportError(VIR_ERR_NO_SOURCE,
target ? "%s" : NULL, target);
@@ -2905,6 +2923,7 @@ virDomainFSDefParseXML(xmlNodePtr node,
cleanup:
VIR_FREE(type);
+ VIR_FREE(fsdriver);
VIR_FREE(target);
VIR_FREE(source);
VIR_FREE(accessmode);
@@ -9351,6 +9370,7 @@ virDomainFSDefFormat(virBufferPtr buf,
{
const char *type = virDomainFSTypeToString(def->type);
const char *accessmode = virDomainFSAccessModeTypeToString(def->accessmode);
+ const char *fsdriver = virDomainFSDriverTypeTypeToString(def->fsdriver);
if (!type) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -9369,6 +9389,10 @@ virDomainFSDefFormat(virBufferPtr buf,
" <filesystem type='%s' accessmode='%s'>\n",
type, accessmode);
+ if (def->fsdriver) {
+ virBufferAsprintf(buf, " <driver type='%s'/>\n", fsdriver);
+ }
+
if (def->src) {
switch (def->type) {
case VIR_DOMAIN_FS_TYPE_MOUNT:
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index e07fd2f..4f8993b 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -368,6 +368,15 @@ enum virDomainFSType {
VIR_DOMAIN_FS_TYPE_LAST
};
+/* Filesystem driver type */
+enum virDomainFSDriverType {
+ VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT = 0,
+ VIR_DOMAIN_FS_DRIVER_TYPE_PATH,
+ VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE,
+
+ VIR_DOMAIN_FS_DRIVER_TYPE_LAST
+};
+
/* Filesystem mount access mode */
enum virDomainFSAccessMode {
VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH,
@@ -381,6 +390,7 @@ typedef struct _virDomainFSDef virDomainFSDef;
typedef virDomainFSDef *virDomainFSDefPtr;
struct _virDomainFSDef {
int type;
+ int fsdriver;
int accessmode;
char *src;
char *dst;
@@ -1856,6 +1866,7 @@ VIR_ENUM_DECL(virDomainController)
VIR_ENUM_DECL(virDomainControllerModelSCSI)
VIR_ENUM_DECL(virDomainControllerModelUSB)
VIR_ENUM_DECL(virDomainFS)
+VIR_ENUM_DECL(virDomainFSDriverType)
VIR_ENUM_DECL(virDomainFSAccessMode)
VIR_ENUM_DECL(virDomainNet)
VIR_ENUM_DECL(virDomainNetBackend)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index cf99f89..112610b 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1818,7 +1818,12 @@ char *qemuBuildFSStr(virDomainFSDefPtr fs,
goto error;
}
- virBufferAddLit(&opt, "local");
+ if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH) {
+ virBufferAddLit(&opt, "local");
+ } else if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE) {
+ virBufferAddLit(&opt, "handle");
+ }
+
if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) {
virBufferAddLit(&opt, ",security_model=mapped");
} else if(fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) {
--
1.7.1.1
13 years, 2 months
[libvirt] libvirt changes/certification questions
by Udayan Bapat
Hi
I am new to open source development and had a question about libvirt/KVM
support in the linux kernel. In general since many people are contributing
to the library, are there any 'standardized regressions or certification
tests' available that make sure that that the existing libvirt
infrastructure does not break with new changes? More particular, how does
one make sure that given kernel version is compatible with a given library
drop?
Thank you
--
Udayan
13 years, 2 months
[libvirt] target for documentation
by Jason Helfman
Hello List,
I was wondering if there are any targets that are just for the documentation
to build and install. I am curious about separating the documentation from
the software from a building and installation point-of-view, and offering
this in the port for FreeBSD. So a user that gives a certain flag will get
everything but the documentation.
Thanks,
Jason
--
Jason Helfman
System Administrator
experts-exchange.com
http://www.experts-exchange.com/M_4830110.html
E4AD 7CF1 1396 27F6 79DD 4342 5E92 AD66 8C8C FBA5
13 years, 2 months
[libvirt] [PATCH] docs: fix html bug
by Eric Blake
</space> doesn't exist. Introduced in commit 4bb4109f.
* docs/formatdomain.html.in: Use correct end tag.
---
Pushing under the trivial rule.
docs/formatdomain.html.in | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index b15f9e2..8daffb8 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1015,7 +1015,7 @@
There is also an
optional <code>rerror_policy</code> that controls behavior
for read errors only. <span class="since">Since
- 0.9.7</space>. If no rerror_policy is given, error_policy
+ 0.9.7</span>. If no rerror_policy is given, error_policy
is used for both read and write errors. If rerror_policy
is given, it overrides the <code>error_policy</code> for
read errors. Also note that "enospace" is not a valid
--
1.7.4.4
13 years, 2 months