From 8a5b3d41b650f3f3b764d0547ed13b895a5a368e Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Wed, 10 Jun 2020 21:22:08 +0200 Subject: [PATCH] implement asyncSendMsg --- asyncutils.nim | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/asyncutils.nim b/asyncutils.nim index 36d23a8..305c600 100644 --- a/asyncutils.nim +++ b/asyncutils.nim @@ -1,4 +1,22 @@ import asyncdispatch, threadpool, osproc +from os import + osLastError, + newOsError +from posix import + SOL_SOCKET, + SCM_RIGHTS, + EINTR, + EWOULDBLOCK, + EAGAIN, + IOVec, + Tmsghdr, + Tcmsghdr, + SocketHandle, + CMSG_SPACE, + CMSG_FIRSTHDR, + CMSG_LEN, + CMSG_DATA, + sendmsg ## asyncReadline as discussed at https://github.com/nim-lang/Nim/issues/11564 proc asyncReadline*(): Future[string] = @@ -26,3 +44,47 @@ proc asyncExecCmd*(command: string): Future[int] = true addEvent(event, callback) return future + +proc asyncSendMsg*(fd: AsyncFD, + data: string, + fds: openArray[AsyncFD] = []): Future[void] = + var retFuture = newFuture[void]("asyncSendMsg") + + proc cb(sock: AsyncFD): bool = + # FIXME: if file descriptors are given in fds, check if sock is a unix socket + result = true + + # sendmsg needs an array of iovec structs as described in the writev(2) man + # page. The message is passed as a msghdr struct which may contain ancillary + # data, see sendmsg(2) man page. We use ancillary data exclusively for + # passing file descriptors if any are given in fds. + var iovec = IOVec(iov_base: data.cstring, + iov_len: data.len.csize_t) + var msg = Tmsghdr(msg_iov: addr iovec, + msg_iovlen: 1) + if fds.len > 0: + # assemble ancillary data, see cmsg(3) man page + let cmsgBufferLen = CMSG_SPACE(sizeof(AsyncFD).csize_t * fds.len.csize_t) + let cmsgBuffer = newString(cmsgBufferLen) + msg.msg_control = cmsgBuffer.cstring + msg.msg_controllen = cmsgBufferLen + let cmsg: ptr Tcmsghdr = CMSG_FIRSTHDR(addr msg) + cmsg.cmsg_len = CMSG_LEN(sizeof(AsyncFD).csize_t * fds.len.csize_t) + cmsg.cmsg_level = SOL_SOCKET + cmsg.cmsg_type = SCM_RIGHTS + copyMem(CMSG_DATA(cmsg), unsafeAddr fds[0], sizeof(AsyncFD) * fds.len) + + let res = sendmsg(sock.SocketHandle, addr msg, 0) + if res < 0: + let lastError = osLastError() + if lastError.int32 != EINTR and + lastError.int32 != EWOULDBLOCK and + lastError.int32 != EAGAIN: + retFuture.fail(newOSError(lastError)) + else: + result = false + else: + retFuture.complete() + + addWrite(fd, cb) + return retFuture