diff --git a/docs/internals/rpc.html.in b/docs/internals/rpc.html.in
index 2c0c9836db7cdc5c0fee731f5261a8623735d734..4cf1e380c534151ab8cf6e4866d91006e779a22f 100644
--- a/docs/internals/rpc.html.in
+++ b/docs/internals/rpc.html.in
@@ -163,14 +163,28 @@
- type=call: the in parameters for the method call, XDR encoded
+ - type=call-with-fds: number of file handles, then the in parameters for the method call, XDR encoded, followed by the file handles
- type=reply+status=ok: the return value and/or out parameters for the method call, XDR encoded
- type=reply+status=error: the error information for the method, a virErrorPtr XDR encoded
+ - type=reply-with-fds+status=ok: number of file handles, the return value and/or out parameters for the method call, XDR encoded, followed by the file handles
+ - type=reply-with-fds+status=error: number of file handles, the error information for the method, a virErrorPtr XDR encoded, followed by the file handles
- type=event: the parameters for the event, XDR encoded
- type=stream+status=ok: no payload
- type=stream+status=error: the error information for the method, a virErrorPtr XDR encoded
- type=stream+status=continue: the raw bytes of data for the stream. No XDR encoding
+
+ With the two packet types that support passing file descriptors, in
+ between the header and the payload there will be a 4-byte integer
+ specifying the number of file descriptors which are being sent.
+ The actual file handles are sent after the payload has been sent.
+ Each file handle has a single dummy byte transmitted as a carrier
+ for the out of band file descriptor. While the sender should always
+ send '\0' as the dummy byte value, the receiver ought to ignore the
+ value for the sake of robustness.
+
+
For the exact payload information for each procedure, consult the XDR protocol
definition for the program+version in question
@@ -339,6 +353,27 @@
+--+-----------------------+--------+
+
+
+
+ A single method call with 2 passed file descriptors and successful
+ reply, for a program=8, version=1, procedure=3, which 10 bytes worth
+ of input args, and 4 bytes worth of return values. The number of
+ file descriptors is encoded as a 32-bit int. Each file descriptor
+ then has a 1 byte dummy payload. The overall input
+ packet length is 4 + 24 + 4 + 2 + 10 == 44, and output packet length 32.
+
+
+
+ +--+-----------------------+---------------+-------+
+ C --> |44| 8 | 1 | 3 | 0 | 1 | 0 | 2 | .o.oOo.o. | 0 | 0 | --> S (call)
+ +--+-----------------------+---------------+-------+
+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | <-- S (reply)
+ +--+-----------------------+--------+
+
+
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 3b4b429cc930afe18d360db433837b138995c3ae..8c74f56bce819e71423620e04203dd14b790f4f5 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1186,8 +1186,10 @@ virFileRewrite;
# virnetmessage.h
virNetMessageClear;
+virNetMessageDecodeNumFDs;
virNetMessageEncodeHeader;
virNetMessageEncodePayload;
+virNetMessageEncodeNumFDs;
virNetMessageFree;
virNetMessageNew;
virNetMessageQueuePush;
diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c
index a1ae9c4d7b5a32c83d1aff2695e165fe573143d7..8abf5ab5bcfea9b9377a79148018fb49e8d3f1c2 100644
--- a/src/rpc/virnetmessage.c
+++ b/src/rpc/virnetmessage.c
@@ -21,11 +21,14 @@
#include
#include
+#include
#include "virnetmessage.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
+#include "virfile.h"
+#include "util.h"
#define VIR_FROM_THIS VIR_FROM_RPC
#define virNetError(code, ...) \
@@ -51,6 +54,13 @@ virNetMessagePtr virNetMessageNew(bool tracked)
void virNetMessageClear(virNetMessagePtr msg)
{
bool tracked = msg->tracked;
+ size_t i;
+
+ VIR_DEBUG("msg=%p nfds=%zu", msg, msg->nfds);
+
+ for (i = 0 ; i < msg->nfds ; i++)
+ VIR_FORCE_CLOSE(msg->fds[i]);
+ VIR_FREE(msg->fds);
memset(msg, 0, sizeof(*msg));
msg->tracked = tracked;
}
@@ -58,14 +68,18 @@ void virNetMessageClear(virNetMessagePtr msg)
void virNetMessageFree(virNetMessagePtr msg)
{
+ size_t i;
if (!msg)
return;
+ VIR_DEBUG("msg=%p nfds=%zu cb=%p", msg, msg->nfds, msg->cb);
+
if (msg->cb)
msg->cb(msg, msg->opaque);
- VIR_DEBUG("msg=%p", msg);
-
+ for (i = 0 ; i < msg->nfds ; i++)
+ VIR_FORCE_CLOSE(msg->fds[i]);
+ VIR_FREE(msg->fds);
VIR_FREE(msg);
}
@@ -239,6 +253,78 @@ cleanup:
}
+int virNetMessageEncodeNumFDs(virNetMessagePtr msg)
+{
+ XDR xdr;
+ unsigned int numFDs = msg->nfds;
+ int ret = -1;
+
+ xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+ msg->bufferLength - msg->bufferOffset, XDR_ENCODE);
+
+ if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) {
+ virNetError(VIR_ERR_RPC,
+ _("Too many FDs to send %d, expected %d maximum"),
+ numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX);
+ goto cleanup;
+ }
+
+ if (!xdr_u_int(&xdr, &numFDs)) {
+ virNetError(VIR_ERR_RPC, "%s", _("Unable to encode number of FDs"));
+ goto cleanup;
+ }
+ msg->bufferOffset += xdr_getpos(&xdr);
+
+ VIR_DEBUG("Send %zu FDs to peer", msg->nfds);
+
+ ret = 0;
+
+cleanup:
+ xdr_destroy(&xdr);
+ return ret;
+}
+
+
+int virNetMessageDecodeNumFDs(virNetMessagePtr msg)
+{
+ XDR xdr;
+ unsigned int numFDs;
+ int ret = -1;
+ size_t i;
+
+ xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+ msg->bufferLength - msg->bufferOffset, XDR_DECODE);
+ if (!xdr_u_int(&xdr, &numFDs)) {
+ virNetError(VIR_ERR_RPC, "%s", _("Unable to decode number of FDs"));
+ goto cleanup;
+ }
+ msg->bufferOffset += xdr_getpos(&xdr);
+
+ if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) {
+ virNetError(VIR_ERR_RPC,
+ _("Received too many FDs %d, expected %d maximum"),
+ numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX);
+ goto cleanup;
+ }
+
+ msg->nfds = numFDs;
+ if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ for (i = 0 ; i < msg->nfds ; i++)
+ msg->fds[i] = -1;
+
+ VIR_DEBUG("Got %zu FDs from peer", msg->nfds);
+
+ ret = 0;
+
+cleanup:
+ xdr_destroy(&xdr);
+ return ret;
+}
+
+
int virNetMessageEncodePayload(virNetMessagePtr msg,
xdrproc_t filter,
void *data)
@@ -403,3 +489,31 @@ void virNetMessageSaveError(virNetMessageErrorPtr rerr)
rerr->level = VIR_ERR_ERROR;
}
}
+
+
+int virNetMessageDupFD(virNetMessagePtr msg,
+ size_t slot)
+{
+ int fd;
+
+ if (slot >= msg->nfds) {
+ virNetError(VIR_ERR_INTERNAL_ERROR,
+ _("No FD available at slot %zu"), slot);
+ return -1;
+ }
+
+ if ((fd = dup(msg->fds[slot])) < 0) {
+ virReportSystemError(errno,
+ _("Unable to duplicate FD %d"),
+ msg->fds[slot]);
+ return -1;
+ }
+ if (virSetInherit(fd, false) < 0) {
+ VIR_FORCE_CLOSE(fd);
+ virReportSystemError(errno,
+ _("Cannot set close-on-exec %d"),
+ fd);
+ return -1;
+ }
+ return fd;
+}
diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h
index 307a0413ef4e90521c971fe50b00c4b9e91de557..ad634099d83f5218f3f9ea24c3adcd46b8cbb3bc 100644
--- a/src/rpc/virnetmessage.h
+++ b/src/rpc/virnetmessage.h
@@ -46,6 +46,9 @@ struct _virNetMessage {
virNetMessageFreeCallback cb;
void *opaque;
+ size_t nfds;
+ int *fds;
+
virNetMessagePtr next;
};
@@ -78,6 +81,9 @@ int virNetMessageDecodePayload(virNetMessagePtr msg,
void *data)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+int virNetMessageEncodeNumFDs(virNetMessagePtr msg);
+int virNetMessageDecodeNumFDs(virNetMessagePtr msg);
+
int virNetMessageEncodePayloadRaw(virNetMessagePtr msg,
const char *buf,
size_t len)
@@ -88,4 +94,7 @@ int virNetMessageEncodePayloadEmpty(virNetMessagePtr msg)
void virNetMessageSaveError(virNetMessageErrorPtr rerr)
ATTRIBUTE_NONNULL(1);
+int virNetMessageDupFD(virNetMessagePtr msg,
+ size_t slot);
+
#endif /* __VIR_NET_MESSAGE_H__ */
diff --git a/src/rpc/virnetprotocol.x b/src/rpc/virnetprotocol.x
index 306fafad5044684118cc8e6d53df54f3175ee520..4520e380da237d72684ea0d8a3c33270b9cff875 100644
--- a/src/rpc/virnetprotocol.x
+++ b/src/rpc/virnetprotocol.x
@@ -62,6 +62,11 @@ const VIR_NET_MESSAGE_LEN_MAX = 4;
*/
const VIR_NET_MESSAGE_STRING_MAX = 65536;
+/* Limit on number of File Descriptors allowed to be
+ * passed per message
+ */
+const VIR_NET_MESSAGE_NUM_FDS_MAX = 32;
+
/*
* RPC wire format
*
@@ -126,6 +131,18 @@ const VIR_NET_MESSAGE_STRING_MAX = 65536;
* remote_error error information
* * status == VIR_NET_OK
*
+ *
+ * - type == VIR_NET_CALL_WITH_FDS
+ * int8 - number of FDs
+ * XXX_args for procedure
+ *
+ * - type == VIR_NET_REPLY_WITH_FDS
+ * int8 - number of FDs
+ * * status == VIR_NET_OK
+ * XXX_ret for procedure
+ * * status == VIR_NET_ERROR
+ * remote_error Error information
+ *
*/
enum virNetMessageType {
/* client -> server. args from a method call */
@@ -135,7 +152,11 @@ enum virNetMessageType {
/* either direction. async notification */
VIR_NET_MESSAGE = 2,
/* either direction. stream data packet */
- VIR_NET_STREAM = 3
+ VIR_NET_STREAM = 3,
+ /* client -> server. args from a method call, with passed FDs */
+ VIR_NET_CALL_WITH_FDS = 4,
+ /* server -> client. reply/error from a method call, with passed FDs */
+ VIR_NET_REPLY_WITH_FDS = 5
};
enum virNetMessageStatus {
diff --git a/src/virnetprotocol-structs b/src/virnetprotocol-structs
index 4d97e9c55a2b85277c9982473707fc114875cece..af4526c9011ad33655cf216b49f9aec06b5d1085 100644
--- a/src/virnetprotocol-structs
+++ b/src/virnetprotocol-structs
@@ -4,6 +4,8 @@ enum virNetMessageType {
VIR_NET_REPLY = 1,
VIR_NET_MESSAGE = 2,
VIR_NET_STREAM = 3,
+ VIR_NET_CALL_WITH_FDS = 4,
+ VIR_NET_REPLY_WITH_FDS = 5,
};
enum virNetMessageStatus {
VIR_NET_OK = 0,