This is nearly the same story as formatted read. With one
exception. Due to the recurring pattern of calling write
functions in our code:
repeat {
nwritten = write(dest, buf + offset, buflen - offset);
offset += nwritten;
}
we have to be cautious about the return value of iohelperWrite().
If we would return number of bytes partially written, we would be
called again with an offset shifted. But at the beginning of
iohelperWrite() we flush the output queue so the remaining data
is written then. Moreover, there's no way for us to determine
whether this is the case or we are called with fresh data,
completely unrelated to the first call.
Therefore, we must keep claiming EAGAIN, even though the message
is being partially written, and just on the last iteration claim
success and return the size of data we were requested to write in
the first place. Even if this means that in the last iteration
just one byte was written to make the message write complete.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/iohelper/iohelper_message.c | 105 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 100 insertions(+), 5 deletions(-)
diff --git a/src/iohelper/iohelper_message.c b/src/iohelper/iohelper_message.c
index fe2304b..d900c2f 100644
--- a/src/iohelper/iohelper_message.c
+++ b/src/iohelper/iohelper_message.c
@@ -39,9 +39,11 @@ struct iohelperCtl {
bool blocking;
virNetMessagePtr msg;
bool msgReadyRead;
+ bool msgReadyWrite;
};
typedef ssize_t (*readfunc)(int fd, void *buf, size_t count);
+typedef ssize_t (*writefunc)(int fd, const void *buf, size_t count);
static virClassPtr iohelperCtlClass;
@@ -84,6 +86,7 @@ iohelperCtlNew(int fd,
ret->fd = fd;
ret->blocking = blocking;
ret->msgReadyRead = false;
+ ret->msgReadyWrite = true;
return ret;
@@ -98,6 +101,7 @@ messageClear(iohelperCtlPtr ctl)
{
virNetMessageClear(ctl->msg);
ctl->msgReadyRead = false;
+ ctl->msgReadyWrite = true;
}
@@ -107,6 +111,11 @@ messageReadyRead(iohelperCtlPtr ctl)
return ctl->msgReadyRead;
}
+static inline bool
+messageReadyWrite(iohelperCtlPtr ctl)
+{
+ return ctl->msgReadyWrite;
+}
static ssize_t
messageRecv(iohelperCtlPtr ctl)
@@ -165,6 +174,47 @@ messageRecv(iohelperCtlPtr ctl)
}
+static ssize_t
+messageSend(iohelperCtlPtr ctl)
+{
+ virNetMessagePtr msg = ctl->msg;
+ writefunc writeF = ctl->blocking ? safewrite : write;
+
+ ctl->msgReadyWrite = false;
+
+ while (true) {
+ ssize_t nwritten;
+ size_t want;
+
+ want = msg->bufferLength - msg->bufferOffset;
+
+ rewrite:
+ errno = 0;
+ nwritten = writeF(ctl->fd,
+ msg->buffer + msg->bufferOffset,
+ want);
+
+ if (nwritten < 0) {
+ if (errno == EINTR)
+ goto rewrite;
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ } else if (nwritten == 0) {
+ /* EOF while writing */
+ return 0;
+ } else {
+ msg->bufferOffset += nwritten;
+ }
+
+ if (msg->bufferOffset == msg->bufferLength) {
+ ctl->msgReadyWrite = true;
+ return msg->bufferLength;
+ }
+ }
+}
+
+
ssize_t
iohelperRead(iohelperCtlPtr ctl,
char *bytes,
@@ -206,12 +256,57 @@ iohelperRead(iohelperCtlPtr ctl,
ssize_t
-iohelperWrite(iohelperCtlPtr ctl ATTRIBUTE_UNUSED,
- const char *bytes ATTRIBUTE_UNUSED,
- size_t nbytes ATTRIBUTE_UNUSED)
+iohelperWrite(iohelperCtlPtr ctl,
+ const char *bytes,
+ size_t nbytes)
{
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("sparse stream not supported"));
+ size_t headerLen;
+ ssize_t nwritten, totalNwritten = 0;
+
+ virNetMessagePtr msg = ctl->msg;
+
+ if (!messageReadyWrite(ctl)) {
+ /* Okay, the outgoing message is not fully sent. Try to
+ * finish the sending and recheck. */
+ if ((nwritten = messageSend(ctl)) < 0)
+ return -1;
+
+ if (!nwritten && errno != EAGAIN)
+ return 0;
+
+ if (!messageReadyWrite(ctl)) {
+ errno = EAGAIN;
+ return -2;
+ }
+
+ totalNwritten += nwritten;
+ }
+
+ memset(&msg->header, 0, sizeof(msg->header));
+ msg->header.type = VIR_NET_STREAM;
+ msg->header.status = nbytes ? VIR_NET_CONTINUE : VIR_NET_OK;
+
+ /* Encoding a message is fatal and we should discard any
+ * partially encoded message. */
+ if (virNetMessageEncodeHeader(msg) < 0)
+ goto error;
+
+ headerLen = msg->bufferOffset;
+
+ if (virNetMessageEncodePayloadRaw(msg, bytes, nbytes) < 0)
+ goto error;
+
+ /* At this point, the message is successfully encoded. Don't
+ * discard it if something below fails. */
+ if ((nwritten = messageSend(ctl)) < 0)
+ return -1;
+
+ totalNwritten += nwritten - headerLen;
+
+ return totalNwritten;
+
+ error:
+ messageClear(ctl);
return -1;
}
--
2.8.4