diff options
author | bptato <nincsnevem662@gmail.com> | 2024-10-20 16:54:40 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-10-20 16:54:40 +0200 |
commit | d29105fa71f315816ce03c1e5f89423b65b3b389 (patch) | |
tree | 7a46c8ce69328ce0ad2659d123e264a8e8cf6c93 /src/io | |
parent | ddb0ba9eb1454b15ba6db7c109ce23c448ddd66e (diff) | |
download | chawan-d29105fa71f315816ce03c1e5f89423b65b3b389.tar.gz |
dynstream: refactor
* consistently use cint instead of FileHandle - this was another remnant of winapi support; on posix, they are the same. * move "blocking" field to PosixStream * recvFileHandle -> recvFd, sendFileHandle -> sendFd * merge serversocket into dynstream * merge auxiliary C functions into dynstream_aux
Diffstat (limited to 'src/io')
-rw-r--r-- | src/io/bind_unix.c | 31 | ||||
-rw-r--r-- | src/io/bufreader.nim | 4 | ||||
-rw-r--r-- | src/io/bufwriter.nim | 4 | ||||
-rw-r--r-- | src/io/connect_unix.c | 31 | ||||
-rw-r--r-- | src/io/dynstream.nim | 108 | ||||
-rw-r--r-- | src/io/dynstream_aux.c | 110 | ||||
-rw-r--r-- | src/io/recvfd.c | 32 | ||||
-rw-r--r-- | src/io/sendfd.c | 28 | ||||
-rw-r--r-- | src/io/serversocket.nim | 74 |
9 files changed, 198 insertions, 224 deletions
diff --git a/src/io/bind_unix.c b/src/io/bind_unix.c deleted file mode 100644 index ea0140cf..00000000 --- a/src/io/bind_unix.c +++ /dev/null @@ -1,31 +0,0 @@ -#include <stddef.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <string.h> - -int bind_unix_from_c(int socket, const char *path, int pathlen) -{ - struct sockaddr_un sa = { - .sun_family = AF_UNIX - }; - int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; - - memcpy(sa.sun_path, path, pathlen + 1); - return bind(socket, (struct sockaddr *)&sa, len); -} - -#ifdef __FreeBSD__ -#include <fcntl.h> - -int bindat_unix_from_c(int fd, int socket, const char *path, int pathlen) -{ - struct sockaddr_un sa = { - .sun_family = AF_UNIX - }; - int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; - - memcpy(sa.sun_path, path, pathlen + 1); - return bindat(fd, socket, (struct sockaddr *)&sa, len); -} -#endif diff --git a/src/io/bufreader.nim b/src/io/bufreader.nim index 7813bd93..c7ec04b1 100644 --- a/src/io/bufreader.nim +++ b/src/io/bufreader.nim @@ -8,7 +8,7 @@ import types/opt type BufferedReader* = object buffer: seq[uint8] bufIdx: int - recvAux*: seq[FileHandle] #TODO assert on unused ones + recvAux*: seq[cint] #TODO assert on unused ones proc sread*(reader: var BufferedReader; n: var SomeNumber) proc sread*[T](reader: var BufferedReader; s: var set[T]) @@ -34,7 +34,7 @@ proc initReader*(stream: DynStream; len, auxLen: int): BufferedReader = ) stream.recvDataLoop(reader.buffer) for i in 0 ..< auxLen: - reader.recvAux.add(SocketStream(stream).recvFileHandle()) + reader.recvAux.add(SocketStream(stream).recvFd()) return reader proc initPacketReader*(stream: DynStream): BufferedReader = diff --git a/src/io/bufwriter.nim b/src/io/bufwriter.nim index 5b07e0d6..9c3d6c79 100644 --- a/src/io/bufwriter.nim +++ b/src/io/bufwriter.nim @@ -13,7 +13,7 @@ type BufferedWriter* = object buffer: ptr UncheckedArray[uint8] bufSize: int bufLen: int - sendAux*: seq[FileHandle] + sendAux*: seq[cint] proc `=destroy`(writer: var BufferedWriter) = if writer.buffer != nil: @@ -53,7 +53,7 @@ proc flush*(writer: var BufferedWriter) = copyMem(writer.buffer, unsafeAddr len[0], sizeof(len)) writer.stream.sendDataLoop(writer.buffer, writer.bufLen) for i in countdown(writer.sendAux.high, 0): - SocketStream(writer.stream).sendFileHandle(writer.sendAux[i]) + SocketStream(writer.stream).sendFd(writer.sendAux[i]) writer.bufLen = 0 writer.stream.sflush() diff --git a/src/io/connect_unix.c b/src/io/connect_unix.c deleted file mode 100644 index f5bdbfa1..00000000 --- a/src/io/connect_unix.c +++ /dev/null @@ -1,31 +0,0 @@ -#include <stddef.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <string.h> - -int connect_unix_from_c(int socket, const char *path, int pathlen) -{ - struct sockaddr_un sa = { - .sun_family = AF_UNIX - }; - int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; - - memcpy(sa.sun_path, path, pathlen + 1); - return connect(socket, (struct sockaddr *)&sa, len); -} - -#ifdef __FreeBSD__ -#include <fcntl.h> - -int connectat_unix_from_c(int fd, int socket, const char *path, int pathlen) -{ - struct sockaddr_un sa = { - .sun_family = AF_UNIX - }; - int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; - - memcpy(sa.sun_path, path, pathlen + 1); - return connectat(fd, socket, (struct sockaddr *)&sa, len); -} -#endif diff --git a/src/io/dynstream.nim b/src/io/dynstream.nim index 792f2ce8..5a106491 100644 --- a/src/io/dynstream.nim +++ b/src/io/dynstream.nim @@ -4,7 +4,6 @@ import std/posix type DynStream* = ref object of RootObj isend*: bool - blocking*: bool #TODO move to posixstream closed: bool # Semantics of this function are those of POSIX read(3): that is, it may return @@ -95,6 +94,7 @@ proc recvAll*(s: DynStream): string = type PosixStream* = ref object of DynStream fd*: cint + blocking*: bool ErrorAgain* = object of IOError ErrorBadFD* = object of IOError @@ -187,11 +187,11 @@ proc safeClose*(ps: PosixStream) = else: ps.sclose() -proc newPosixStream*(fd: FileHandle): PosixStream = - return PosixStream(fd: cint(fd), blocking: true) +proc newPosixStream*(fd: cint): PosixStream = + return PosixStream(fd: fd, blocking: true) proc newPosixStream*(fd: SocketHandle): PosixStream = - return PosixStream(fd: cint(fd), blocking: true) + return newPosixStream(cint(fd)) proc newPosixStream*(path: string; flags, mode: cint): PosixStream = let fd = open(cstring(path), flags, mode) @@ -299,37 +299,42 @@ proc drain*(ps: PosixStream) = type SocketStream* = ref object of PosixStream -{.compile: "sendfd.c".} +# Auxiliary functions in C, because writing them portably in Nim is +# a pain. +{.compile: "dynstream_aux.c".} + +proc bind_unix_from_c(fd: cint; path: cstring; pathlen: cint): cint {.importc.} +proc connect_unix_from_c(fd: cint; path: cstring; pathlen: cint): cint + {.importc.} + +when defined(freebsd): + # capsicum stuff + proc unlinkat(dfd: cint; path: cstring; flag: cint): cint + {.importc, header: "<unistd.h>".} + proc bindat_unix_from_c(dfd, sock: cint; path: cstring; pathlen: cint): cint + {.importc.} + proc connectat_unix_from_c(baseFd, sockFd: cint; rel_path: cstring; + rel_pathlen: cint): cint {.importc.} + proc sendfd(sock, fd: cint): int {.importc.} +proc recvfd(sock: cint; fdout: var cint): int {.importc.} -proc sendFileHandle*(s: SocketStream; fd: FileHandle) = - let n = sendfd(s.fd, cint(fd)) +proc sendFd*(s: SocketStream; fd: cint) = + let n = sendfd(s.fd, fd) if n < 0: raisePosixIOError() assert n == 1 # we send a single nul byte as buf -{.compile: "recvfd.c".} -proc recvfd(sock: cint; fdout: ptr cint): int {.importc.} - -proc recvFileHandle*(s: SocketStream): FileHandle = +proc recvFd*(s: SocketStream): cint = var fd: cint - let n = recvfd(s.fd, addr fd) + let n = recvfd(s.fd, fd) if n < 0: raisePosixIOError() - return FileHandle(fd) + return fd method seek*(s: SocketStream; off: int) = doAssert false -# see serversocket.nim for an explanation -{.compile: "connect_unix.c".} -proc connect_unix_from_c(fd: cint; path: cstring; pathlen: cint): cint - {.importc.} -when defined(freebsd): - # for FreeBSD/capsicum - proc connectat_unix_from_c(baseFd, sockFd: cint; rel_path: cstring; - rel_pathlen: cint): cint {.importc.} - const SocketPathPrefix = "cha_sock_" proc getSocketName*(pid: int): string = SocketPathPrefix & $pid @@ -417,7 +422,7 @@ proc reallyFlush*(s: BufStream) = s.source.sendDataLoop(s.writeBuffer) proc newBufStream*(ps: PosixStream; registerFun: proc(fd: int)): BufStream = - return BufStream(source: ps, blocking: ps.blocking, registerFun: registerFun) + return BufStream(source: ps, registerFun: registerFun) type DynFileStream* = ref object of DynStream @@ -446,10 +451,65 @@ method sflush*(s: DynFileStream) = s.file.flushFile() proc newDynFileStream*(file: File): DynFileStream = - return DynFileStream(file: file, blocking: true) + return DynFileStream(file: file) proc newDynFileStream*(path: string): DynFileStream = var file: File if file.open(path): return newDynFileStream(path) return nil + +type ServerSocket* = ref object + fd*: cint + path*: string + dfd: cint + +proc setBlocking*(ssock: ServerSocket; blocking: bool) = + let ofl = fcntl(ssock.fd, F_GETFL, 0) + if blocking: + discard fcntl(ssock.fd, F_SETFL, ofl and not O_NONBLOCK) + else: + discard fcntl(ssock.fd, F_SETFL, ofl and O_NONBLOCK) + +proc newServerSocket*(fd: cint; sockDir: string; sockDirFd: cint; pid: int): + ServerSocket = + let path = getSocketPath(sockDir, pid) + return ServerSocket(fd: cint(fd), path: path, dfd: sockDirFd) + +proc newServerSocket*(sockDir: string; sockDirFd: cint; pid: int): ServerSocket = + let fd = cint(socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)) + let ssock = newServerSocket(fd, sockDir, sockDirFd, pid) + if sockDirFd == -1: + discard tryRemoveFile(ssock.path) + if bind_unix_from_c(fd, cstring(ssock.path), cint(ssock.path.len)) != 0: + raiseOSError(osLastError()) + else: + when defined(freebsd): + let name = getSocketName(pid) + discard unlinkat(sockDirFd, cstring(name), 0) + if bindat_unix_from_c(sockDirFd, fd, cstring(name), cint(name.len)) != 0: + raiseOSError(osLastError()) + else: + # shouldn't have sockDirFd on other architectures + doAssert false + if listen(SocketHandle(fd), 128) != 0: + raiseOSError(osLastError()) + return ssock + +proc close*(ssock: ServerSocket; unlink = true) = + discard close(ssock.fd) + if unlink: + when defined(freebsd): + if ssock.dfd != -1: + discard unlinkat(ssock.dfd, cstring(ssock.path), 0) + return + discard tryRemoveFile(ssock.path) + +proc acceptSocketStream*(ssock: ServerSocket; blocking = true): SocketStream = + let fd = cint(accept(SocketHandle(ssock.fd), nil, nil)) + if fd == -1: + return nil + let ss = SocketStream(fd: fd, blocking: false) + if not blocking: + ss.setBlocking(false) + return ss diff --git a/src/io/dynstream_aux.c b/src/io/dynstream_aux.c new file mode 100644 index 00000000..651157f8 --- /dev/null +++ b/src/io/dynstream_aux.c @@ -0,0 +1,110 @@ +#include <stddef.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <string.h> + +int bind_unix_from_c(int socket, const char *path, int pathlen) +{ + struct sockaddr_un sa = { + .sun_family = AF_UNIX + }; + int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; + + memcpy(sa.sun_path, path, pathlen + 1); + return bind(socket, (struct sockaddr *)&sa, len); +} + +int connect_unix_from_c(int socket, const char *path, int pathlen) +{ + struct sockaddr_un sa = { + .sun_family = AF_UNIX + }; + int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; + + memcpy(sa.sun_path, path, pathlen + 1); + return connect(socket, (struct sockaddr *)&sa, len); +} + +#ifdef __FreeBSD__ +#include <fcntl.h> + +int bindat_unix_from_c(int fd, int socket, const char *path, int pathlen) +{ + struct sockaddr_un sa = { + .sun_family = AF_UNIX + }; + int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; + + memcpy(sa.sun_path, path, pathlen + 1); + return bindat(fd, socket, (struct sockaddr *)&sa, len); +} + +int connectat_unix_from_c(int fd, int socket, const char *path, int pathlen) +{ + struct sockaddr_un sa = { + .sun_family = AF_UNIX + }; + int len = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; + + memcpy(sa.sun_path, path, pathlen + 1); + return connectat(fd, socket, (struct sockaddr *)&sa, len); +} +#endif + +/* + * See https://stackoverflow.com/a/4491203 + * Send a file handle to socket `sock`. + * Returns: 1 on success, -1 on error. I *think* this never returns 0. */ +ssize_t sendfd(int sock, int fd) +{ + struct msghdr hdr; + struct iovec iov; + int cmsgbuf[CMSG_SPACE(sizeof(int))]; + char buf = '\0'; + struct cmsghdr *cmsg; + + memset(&hdr, 0, sizeof(hdr)); + iov.iov_base = &buf; + iov.iov_len = 1; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = &cmsgbuf[0]; + hdr.msg_controllen = CMSG_LEN(sizeof(fd)); + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *((int *)CMSG_DATA(cmsg)) = fd; + return sendmsg(sock, &hdr, 0); +} + +/* + * Receive a file handle from socket `sock`. + * Sets `fd` to the result if recvmsg returns sizeof(int), otherwise to -1. + * Returns: the return value of recvmsg; this may be -1. */ +ssize_t recvfd(int sock, int *fd) +{ + ssize_t n; + struct iovec iov; + struct msghdr hdr; + int cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + char buf = '\0'; + + iov.iov_base = &buf; + iov.iov_len = 1; + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = &cmsgbuf[0]; + hdr.msg_controllen = CMSG_SPACE(sizeof(int)); + n = recvmsg(sock, &hdr, 0); + if (n <= 0) { + *fd = -1; + return n; + } + cmsg = CMSG_FIRSTHDR(&hdr); + *fd = *((int *)CMSG_DATA(cmsg)); + return n; +} diff --git a/src/io/recvfd.c b/src/io/recvfd.c deleted file mode 100644 index de376c39..00000000 --- a/src/io/recvfd.c +++ /dev/null @@ -1,32 +0,0 @@ -#include <sys/socket.h> -#include <string.h> - -/* See https://stackoverflow.com/a/4491203 - * Receive a file handle from socket `sock`. - * Sets `fd` to the result if recvmsg returns sizeof(int), otherwise to -1. - * Returns: the return value of recvmsg; this may be -1. */ -ssize_t recvfd(int sock, int *fd) -{ - ssize_t n; - struct iovec iov; - struct msghdr hdr; - int cmsgbuf[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; - char buf = '\0'; - - iov.iov_base = &buf; - iov.iov_len = 1; - memset(&hdr, 0, sizeof(hdr)); - hdr.msg_iov = &iov; - hdr.msg_iovlen = 1; - hdr.msg_control = &cmsgbuf[0]; - hdr.msg_controllen = CMSG_SPACE(sizeof(int)); - n = recvmsg(sock, &hdr, 0); - if (n <= 0) { - *fd = -1; - return n; - } - cmsg = CMSG_FIRSTHDR(&hdr); - *fd = *((int *)CMSG_DATA(cmsg)); - return n; -} diff --git a/src/io/sendfd.c b/src/io/sendfd.c deleted file mode 100644 index 578009c2..00000000 --- a/src/io/sendfd.c +++ /dev/null @@ -1,28 +0,0 @@ -#include <sys/socket.h> -#include <string.h> - -/* See https://stackoverflow.com/a/4491203 - * Send a file handle to socket `sock`. - * Returns: 1 on success, -1 on error. I *think* this never returns * 0. */ -ssize_t sendfd(int sock, int fd) -{ - struct msghdr hdr; - struct iovec iov; - int cmsgbuf[CMSG_SPACE(sizeof(int))]; - char buf = '\0'; - struct cmsghdr *cmsg; - - memset(&hdr, 0, sizeof(hdr)); - iov.iov_base = &buf; - iov.iov_len = 1; - hdr.msg_iov = &iov; - hdr.msg_iovlen = 1; - hdr.msg_control = &cmsgbuf[0]; - hdr.msg_controllen = CMSG_LEN(sizeof(fd)); - cmsg = CMSG_FIRSTHDR(&hdr); - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - *((int *)CMSG_DATA(cmsg)) = fd; - return sendmsg(sock, &hdr, 0); -} diff --git a/src/io/serversocket.nim b/src/io/serversocket.nim deleted file mode 100644 index a5b0fd67..00000000 --- a/src/io/serversocket.nim +++ /dev/null @@ -1,74 +0,0 @@ -import std/os -import std/posix - -import io/dynstream - -type ServerSocket* = ref object - fd*: cint - path*: string - dfd: int #TODO should be cint - -# The way stdlib does bindUnix is utterly broken at least on FreeBSD. -# It seems that just writing it in C is the easiest solution. -{.compile: "bind_unix.c".} -proc bind_unix_from_c(fd: cint; path: cstring; pathlen: cint): cint - {.importc.} - -when defined(freebsd): - # capsicum stuff - proc unlinkat(dfd: cint; path: cstring; flag: cint): cint - {.importc, header: "<unistd.h>".} - proc bindat_unix_from_c(dfd, sock: cint; path: cstring; pathlen: cint): cint - {.importc.} - -proc setBlocking*(ssock: ServerSocket; blocking: bool) = - let ofl = fcntl(ssock.fd, F_GETFL, 0) - if blocking: - discard fcntl(ssock.fd, F_SETFL, ofl and not O_NONBLOCK) - else: - discard fcntl(ssock.fd, F_SETFL, ofl and O_NONBLOCK) - -proc newServerSocket*(fd: cint; sockDir: string; pid, sockDirFd: int): - ServerSocket = - let path = getSocketPath(sockDir, pid) - return ServerSocket(fd: cint(fd), path: path, dfd: sockDirFd) - -proc newServerSocket*(sockDir: string; sockDirFd, pid: int): ServerSocket = - let fd = cint(socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)) - let path = getSocketPath(sockDir, pid) - let ssock = ServerSocket(fd: fd, path: path, dfd: sockDirFd) - if sockDirFd == -1: - discard tryRemoveFile(path) - if bind_unix_from_c(fd, cstring(path), cint(path.len)) != 0: - raiseOSError(osLastError()) - else: - when defined(freebsd): - let name = getSocketName(pid) - discard unlinkat(cint(sockDirFd), cstring(name), 0) - if bindat_unix_from_c(cint(sockDirFd), fd, cstring(name), - cint(name.len)) != 0: - raiseOSError(osLastError()) - else: - # shouldn't have sockDirFd on other architectures - doAssert false - if listen(SocketHandle(fd), 128) != 0: - raiseOSError(osLastError()) - return ssock - -proc close*(ssock: ServerSocket; unlink = true) = - discard close(ssock.fd) - if unlink: - when defined(freebsd): - if ssock.dfd != -1: - discard unlinkat(cint(ssock.dfd), cstring(ssock.path), 0) - return - discard tryRemoveFile(ssock.path) - -proc acceptSocketStream*(ssock: ServerSocket; blocking = true): SocketStream = - let fd = cint(accept(SocketHandle(ssock.fd), nil, nil)) - if fd == -1: - return nil - let ss = SocketStream(fd: fd, blocking: false) - if not blocking: - ss.setBlocking(false) - return ss |