+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+ ret = write_all(fd, msg, ret);
+ free(msg);
+ return ret;
+}
+
+/**
+ * Read from a non-blocking file descriptor into multiple buffers.
+ *
+ * \param fd The file descriptor to read from.
+ * \param iov Scatter/gather array used in readv().
+ * \param iovcnt Number of elements in \a iov.
+ * \param num_bytes Result pointer. Contains the number of bytes read from \a fd.
+ *
+ * This function tries to read up to sz bytes from fd, where sz is the sum of
+ * the lengths of all vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are
+ * not considered error conditions. However, EOF is.
+ *
+ * \return Zero or a negative error code. If the underlying call to readv(2)
+ * returned zero (indicating an end of file condition) or failed for some
+ * reason other than EAGAIN or EINTR, a negative error code is returned.
+ *
+ * In any case, \a num_bytes contains the number of bytes that have been
+ * successfully read from \a fd (zero if the first readv() call failed with
+ * EAGAIN). Note that even if the function returns negative, some data might
+ * have been read before the error occurred. In this case \a num_bytes is
+ * positive.
+ *
+ * \sa \ref xwrite(), read(2), readv(2).
+ */
+int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes)
+{
+ int ret, i, j;
+
+ *num_bytes = 0;
+ for (i = 0, j = 0; i < iovcnt;) {
+ /* fix up the first iov */
+ assert(j < iov[i].iov_len);
+ iov[i].iov_base += j;
+ iov[i].iov_len -= j;
+ ret = readv(fd, iov + i, iovcnt - i);
+ iov[i].iov_base -= j;
+ iov[i].iov_len += j;
+
+ if (ret == 0)
+ return -E_EOF;
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ return -ERRNO_TO_PARA_ERROR(errno);
+ }
+ *num_bytes += ret;
+ while (ret > 0) {
+ if (ret < iov[i].iov_len - j) {
+ j += ret;
+ break;
+ }
+ ret -= iov[i].iov_len - j;
+ j = 0;
+ if (++i >= iovcnt)
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Read from a non-blocking file descriptor into a single buffer.
+ *
+ * \param fd The file descriptor to read from.
+ * \param buf The buffer to read data to.
+ * \param sz The size of \a buf.
+ * \param num_bytes \see \ref readv_nonblock().
+ *
+ * This is a simple wrapper for readv_nonblock() which uses an iovec with a single
+ * buffer.
+ *
+ * \return The return value of the underlying call to readv_nonblock().
+ */
+int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes)
+{
+ struct iovec iov = {.iov_base = buf, .iov_len = sz};
+ return readv_nonblock(fd, &iov, 1, num_bytes);
+}
+
+/**
+ * Read a buffer and compare its contents to a string, ignoring case.
+ *
+ * \param fd The file descriptor to read from.
+ * \param expectation The expected string to compare to.
+ *
+ * The given file descriptor is expected to be in non-blocking mode. The string
+ * comparison is performed using strncasecmp(3).
+ *
+ * \return Zero if no data was available, positive if a buffer was read whose
+ * contents compare as equal to the expected string, negative otherwise.
+ * Possible errors: (a) not enough data was read, (b) the buffer contents
+ * compared as non-equal, (c) a read error occurred. In the first two cases,
+ * -E_READ_PATTERN is returned. In the read error case the (negative) return
+ * value of the underlying call to \ref read_nonblock() is returned.
+ */
+int read_and_compare(int fd, const char *expectation)
+{
+ size_t n, len = strlen(expectation);
+ char *buf = alloc(len + 1);
+ int ret = read_nonblock(fd, buf, len, &n);
+