[libvirt] [PATCH 02/15] Provide a simple object for encoding/decoding RPC messages

Daniel P. Berrange berrange at redhat.com
Thu Dec 16 11:21:54 UTC 2010


This provides a new struct that contains a buffer for the RPC
message header+payload, as well as a decoded copy of the message
header. There is an API for applying a XDR encoding & decoding
of the message headers and payloads. There are also APIs for
maintaining a simple FIFO queue of message instances.

Expected usage scenarios are:

To send a message

   msg = virNetMessageNew()

   ...fill in msg->header fields..
   virNetMessageEncodeHeader(msg)
   ...loook at msg->header fields to determine payload filter
   virNetMessageEncodePayload(msg, xdrfilter, data)
   ...send msg->bufferLength worth of data from buffer

To receive a message

   msg = virNetMessageNew()
   ...read VIR_NET_MESSAGE_LEN_MAX of data into buffer
   virNetMessageDecodeLength(msg)
   ...read msg->bufferLength-msg->bufferOffset of data into buffer
   virNetMessageDecodeHeader(msg)
   ...look at msg->header fields to determine payload filter
   virNetMessageDecodePayload(msg, xdrfilter, data)
   ...run payload processor

* src/Makefile.am: Add to libvirt-net-rpc.la
* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h: Internal
  message handling API.
---
 po/POTFILES.in          |    1 +
 src/Makefile.am         |    1 +
 src/rpc/virnetmessage.c |  325 +++++++++++++++++++++++++++++++++++++++++++++++
 src/rpc/virnetmessage.h |   68 ++++++++++
 4 files changed, 395 insertions(+), 0 deletions(-)
 create mode 100644 src/rpc/virnetmessage.c
 create mode 100644 src/rpc/virnetmessage.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index e7be0d3..c79234a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -58,6 +58,7 @@ src/qemu/qemu_monitor_json.c
 src/qemu/qemu_monitor_text.c
 src/qemu/qemu_security_dac.c
 src/remote/remote_driver.c
+src/rpc/virnetmessage.c
 src/secret/secret_driver.c
 src/security/security_apparmor.c
 src/security/security_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 8837b43..daf7643 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1121,6 +1121,7 @@ EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE)
 noinst_LTLIBRARIES += libvirt-net-rpc.la
 
 libvirt_net_rpc_la_SOURCES = \
+	rpc/virnetmessage.h rpc/virnetmessage.c \
 	rpc/virnetprotocol.h rpc/virnetprotocol.c
 libvirt_net_rpc_la_CFLAGS = \
 			$(AM_CFLAGS)
diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c
new file mode 100644
index 0000000..c675cfa
--- /dev/null
+++ b/src/rpc/virnetmessage.c
@@ -0,0 +1,325 @@
+/*
+ * virnetmessage.h: basic RPC message encoding/decoding
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include <config.h>
+
+#include "virnetmessage.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+#define virNetError(code, ...)                                    \
+    virReportErrorHelper(NULL, VIR_FROM_RPC, code, __FILE__,      \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+virNetMessagePtr virNetMessageNew(void)
+{
+    virNetMessagePtr msg;
+
+    if (VIR_ALLOC(msg) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    VIR_DEBUG("msg=%p", msg);
+
+    return msg;
+}
+
+void virNetMessageFree(virNetMessagePtr msg)
+{
+    if (!msg)
+        return;
+
+    VIR_DEBUG("msg=%p", msg);
+
+    VIR_FREE(msg);
+}
+
+void virNetMessageQueuePush(virNetMessagePtr *queue, virNetMessagePtr msg)
+{
+    virNetMessagePtr tmp = *queue;
+
+    if (tmp) {
+        while (tmp->next)
+            tmp = tmp->next;
+        tmp->next = msg;
+    } else {
+        *queue = msg;
+    }
+}
+
+
+virNetMessagePtr virNetMessageQueueServe(virNetMessagePtr *queue)
+{
+    virNetMessagePtr tmp = *queue;
+
+    if (tmp) {
+        *queue = tmp->next;
+        tmp->next = NULL;
+    }
+
+    return tmp;
+}
+
+
+int virNetMessageDecodeLength(virNetMessagePtr msg)
+{
+    XDR xdr;
+    unsigned int len;
+    int ret = -1;
+
+    xdrmem_create(&xdr, msg->buffer,
+                  msg->bufferLength, XDR_DECODE);
+    if (!xdr_u_int(&xdr, &len)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode message length"));
+        goto cleanup;
+    }
+    msg->bufferOffset = xdr_getpos(&xdr);
+
+    if (len < VIR_NET_MESSAGE_LEN_MAX) {
+        virNetError(VIR_ERR_RPC, "%s",
+                    _("packet received from server too small"));
+        goto cleanup;
+    }
+
+    /* Length includes length word - adjust to real length to read. */
+    len -= VIR_NET_MESSAGE_LEN_MAX;
+
+    if (len > VIR_NET_MESSAGE_MAX) {
+        virNetError(VIR_ERR_RPC, "%s",
+                    _("packet received from server too large"));
+        goto cleanup;
+    }
+
+    /* Extend our declared buffer length and carry
+       on reading the header + payload */
+    msg->bufferLength += len;
+
+    VIR_DEBUG("Got length, now need %zu total (%u more)",
+              msg->bufferLength, len);
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+/*
+ * @msg: the complete incoming message, whose header to decode
+ *
+ * Decodes the header part of the  message, but does not
+ * validate the decoded fields in the header. It expects
+ * bufferLength to refer to length of the data packet. Upon
+ * return bufferOffset will refer to the amount of the packet
+ * consumed by decoding of the header.
+ *
+ * returns 0 if successfully decoded, -1 upon fatal error
+ */
+int virNetMessageDecodeHeader(virNetMessagePtr msg)
+{
+    XDR xdr;
+    int ret = -1;
+
+    msg->bufferOffset = VIR_NET_MESSAGE_LEN_MAX;
+
+    /* Parse the header. */
+    xdrmem_create(&xdr,
+                  msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset,
+                  XDR_DECODE);
+
+    if (!xdr_virNetMessageHeader(&xdr, &msg->header)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode message header"));
+        goto cleanup;
+    }
+
+    msg->bufferOffset += xdr_getpos(&xdr);
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+/*
+ * @msg: the outgoing message, whose header to encode
+ *
+ * Encodes the length word and header of the  message, setting the
+ * message offset ready to encode the payload. Leaves space
+ * for the length field later. Upon return bufferLength will
+ * refer to the total available space for message, while
+ * bufferOffset will refer to current space used by header
+ *
+ * returns 0 if successfully encoded, -1 upon fatal error
+ */
+int virNetMessageEncodeHeader(virNetMessagePtr msg)
+{
+    XDR xdr;
+    int ret = -1;
+    unsigned int len = 0;
+
+    msg->bufferLength = sizeof(msg->buffer);
+    msg->bufferOffset = 0;
+
+    /* Format the header. */
+    xdrmem_create(&xdr,
+                  msg->buffer,
+                  msg->bufferLength,
+                  XDR_ENCODE);
+
+    /* The real value is filled in shortly */
+    if (!xdr_u_int(&xdr, &len)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message length"));
+        goto cleanup;
+    }
+
+    if (!xdr_virNetMessageHeader(&xdr, &msg->header)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message header"));
+        goto cleanup;
+    }
+
+    len = xdr_getpos(&xdr);
+    xdr_setpos(&xdr, 0);
+
+    /* Fill in current length - may be re-written later
+     * if a payload is added
+     */
+    if (!xdr_u_int(&xdr, &len)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to re-encode message length"));
+        goto cleanup;
+    }
+
+    msg->bufferOffset += len;
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+int virNetMessageEncodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data)
+{
+    XDR xdr;
+    unsigned int msglen;
+
+    /* Serialise header followed by args. */
+    xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset, XDR_ENCODE);
+
+    if (!(*filter)(&xdr, data)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message payload"));
+        goto error;
+    }
+
+    /* Get the length stored in buffer. */
+    msg->bufferOffset += xdr_getpos(&xdr);
+    xdr_destroy(&xdr);
+
+    /* Re-encode the length word. */
+    VIR_DEBUG("Encode length as %zu", msg->bufferOffset);
+    xdrmem_create(&xdr, msg->buffer, VIR_NET_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE);
+    msglen = msg->bufferOffset;
+    if (!xdr_u_int(&xdr, &msglen)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message length"));
+        goto error;
+    }
+    xdr_destroy(&xdr);
+
+    msg->bufferLength = msg->bufferOffset;
+    msg->bufferOffset = 0;
+    return 0;
+
+error:
+    xdr_destroy(&xdr);
+    return -1;
+}
+
+
+int virNetMessageDecodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data)
+{
+    XDR xdr;
+
+    /* Serialise header followed by args. */
+    xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset, XDR_DECODE);
+
+    if (!(*filter)(&xdr, data)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode message payload"));
+        goto error;
+    }
+
+    /* Get the length stored in buffer. */
+    msg->bufferLength += xdr_getpos(&xdr);
+    xdr_destroy(&xdr);
+    return 0;
+
+error:
+    xdr_destroy(&xdr);
+    return -1;
+}
+
+
+int virNetMessageEncodePayloadRaw(virNetMessagePtr msg,
+                                  const char *data,
+                                  size_t len)
+{
+    XDR xdr;
+    unsigned int msglen;
+
+    if ((msg->bufferLength - msg->bufferOffset) < len) {
+        virNetError(VIR_ERR_RPC,
+                    _("Stream data too long to send (%zu bytes needed, %zu bytes available)"),
+                    len, (msg->bufferLength - msg->bufferOffset));
+        return -1;
+    }
+
+    memcpy(msg->buffer + msg->bufferOffset, data, len);
+    msg->bufferOffset += len;
+
+    /* Re-encode the length word. */
+    VIR_DEBUG("Encode length as %zu", msg->bufferOffset);
+    xdrmem_create(&xdr, msg->buffer, VIR_NET_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE);
+    msglen = msg->bufferOffset;
+    if (!xdr_u_int(&xdr, &msglen)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message length"));
+        goto error;
+    }
+    xdr_destroy(&xdr);
+
+    msg->bufferLength = msg->bufferOffset;
+    msg->bufferOffset = 0;
+    return 0;
+
+error:
+    xdr_destroy(&xdr);
+    return -1;
+}
diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h
new file mode 100644
index 0000000..a36c618
--- /dev/null
+++ b/src/rpc/virnetmessage.h
@@ -0,0 +1,68 @@
+/*
+ * virnetmessage.h: basic RPC message encoding/decoding
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __VIR_NET_MESSAGE_H__
+# define __VIR_NET_MESSAGE_H__
+
+# include <stdbool.h>
+
+# include "virnetprotocol.h"
+
+typedef struct virNetMessageHeader *virNetMessageHeaderPtr;
+typedef struct virNetMessageError *virNetMessageErrorPtr;
+
+typedef struct _virNetMessage virNetMessage;
+typedef virNetMessage *virNetMessagePtr;
+
+struct _virNetMessage {
+    char buffer[VIR_NET_MESSAGE_MAX + VIR_NET_MESSAGE_LEN_MAX];
+    size_t bufferLength;
+    size_t bufferOffset;
+
+    virNetMessageHeader header;
+
+    virNetMessagePtr next;
+};
+
+
+virNetMessagePtr virNetMessageNew(void);
+
+void virNetMessageFree(virNetMessagePtr msg);
+
+virNetMessagePtr virNetMessageQueueServe(virNetMessagePtr *queue);
+void virNetMessageQueuePush(virNetMessagePtr *queue,
+                            virNetMessagePtr msg);
+
+int virNetMessageEncodeHeader(virNetMessagePtr msg);
+int virNetMessageDecodeLength(virNetMessagePtr msg);
+int virNetMessageDecodeHeader(virNetMessagePtr msg);
+
+int virNetMessageEncodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data);
+int virNetMessageDecodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data);
+
+int virNetMessageEncodePayloadRaw(virNetMessagePtr msg,
+                                  const char *buf,
+                                  size_t len);
+
+#endif /* __VIR_NET_MESSAGE_H__ */
-- 
1.7.2.3




More information about the libvir-list mailing list