posix-api-0.3.4.0: cbits/HaskellPosix.c
#define _GNU_SOURCE
#include <mqueue.h>
#include <netinet/in.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "HaskellPosix.h"
#include "Rts.h"
#ifdef __GNUC__
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#define MAX_BYTEARRAYS 64
// Generally, this library tries to avoid wrapping POSIX functions
// in an additional function. However, for some functions whose wrappers
// unsafe FFI wrappers use unpinned ByteArray instead of Addr, the only
// way to support providing an offset (without just copying the bytes
// into pinned memory) is to use a wrapper.
ssize_t write_offset(int fd, const char *message, HsInt offset, size_t length){
return write(fd, (const void*)(message + offset), length);
}
ssize_t recv_offset(int socket, char *buffer, int offset, size_t length, int flags) {
return recv(socket, (void*)(buffer + offset), length, flags);
}
ssize_t send_offset(int socket, const char *buffer, int offset, size_t length, int flags) {
return send(socket, (const void*)(buffer + offset), length, flags);
}
ssize_t sendto_offset(int socket, const char *message, int offset, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len){
return sendto(socket, (const void*)(message + offset), length, flags, dest_addr, dest_len);
}
ssize_t sendto_inet_offset(int socket, const char *message, int offset, size_t length, int flags, uint16_t port, uint32_t inet_addr){
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr;
dest.sin_port = port;
return sendto(socket, (const void*)(message + offset), length, flags, (struct sockaddr*)&dest, sizeof(dest));
}
ssize_t sendto_inet_addr(int socket, const void *message, size_t length, int flags, uint16_t port, uint32_t inet_addr){
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr;
dest.sin_port = port;
return sendto(socket, message, length, flags, (struct sockaddr*)&dest, sizeof(dest));
}
ssize_t recvfrom_offset(int socket, char *restrict buffer, int offset, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len) {
return recvfrom(socket, (void*)(buffer + offset), length, flags, address, address_len);
}
ssize_t recvfrom_offset_peerless(int socket, char *restrict buffer, int offset, size_t length, int flags) {
return recvfrom(socket, (void*)(buffer + offset), length, flags, NULL, NULL);
}
ssize_t recvfrom_addr_peerless(int socket, void *restrict buffer, size_t length, int flags) {
return recvfrom(socket, buffer, length, flags, NULL, NULL);
}
ssize_t recvfrom_offset_inet
( int socket
, char *restrict buffer_base
, HsInt offset
, size_t length
, int flags
, struct sockaddr_in *restrict addresses
, HsInt address_offset
) {
void* buffer = (void*)(buffer_base + offset);
struct sockaddr_in* address = addresses + address_offset;
socklen_t address_len[1] = {sizeof(struct sockaddr_in)};
ssize_t r = recvfrom(socket, buffer, length, flags, address, address_len);
if (likely(address_len[0] == sizeof(struct sockaddr_in))) {
return r;
} else {
fprintf(stderr, "posix-api: recvfrom_offset_bufs");
exit(EXIT_FAILURE);
}
}
ssize_t recvfrom_offset_inet_addr
( int socket
, void *restrict buffer
, size_t length
, int flags
, struct sockaddr_in *restrict addresses
, HsInt address_offset
) {
struct sockaddr_in* address = addresses + address_offset;
socklen_t address_len[1] = {sizeof(struct sockaddr_in)};
ssize_t r = recvfrom(socket, buffer, length, flags, address, address_len);
if (likely(address_len[0] == sizeof(struct sockaddr_in))) {
return r;
} else {
fprintf(stderr, "posix-api: recvfrom_offset_bufs");
exit(EXIT_FAILURE);
}
}
int setsockopt_int(int socket, int level, int option_name, int option_value) {
return setsockopt(socket,level,option_name,&option_value,sizeof(int));
}
ssize_t sendmsg_bytearrays
( int sockfd
, StgMutArrPtrs *arr // used for input
, HsInt off // offset into input chunk array
, HsInt len0 // number of chunks to send
, HsInt offC // offset into first chunk
, int flags
) {
StgClosure **arrPayload = arr->payload;
StgArrBytes **arrs = (StgArrBytes**)arrPayload;
struct iovec bufs[MAX_BYTEARRAYS];
HsInt len1 = len0 > MAX_BYTEARRAYS ? MAX_BYTEARRAYS : len0;
// We must handle the first chunk specially since
// the user can provide an offset into it.
if(len1 > 0) {
bufs[0].iov_base =
(void*)(((char*)(arrs[off]->payload)) + offC);
bufs[0].iov_len =
(size_t)(((HsInt)(arrs[off]->bytes)) - offC);
}
for (HsInt i = 1; i < len1; i++) {
bufs[i].iov_base = (void*)(arrs[off + i]->payload);
bufs[i].iov_len = (size_t)(arrs[off + i]->bytes);
}
// The msg_flags field is not used when sending.
// Consequently, we do not write to it or read from it.
struct msghdr msg =
{ .msg_name = NULL
, .msg_namelen = 0
, .msg_iov = bufs
, .msg_iovlen = (size_t)len1
, .msg_control = NULL
, .msg_controllen = 0
};
return sendmsg(sockfd,&msg,flags);
}
// The second buffer is char* instead of void* because we need
// to apply an offset to it.
ssize_t sendmsg_a
( int sockfd
, void *bufA
, size_t lenA
, char *bufB
, HsInt offB
, size_t lenB
, int flags
) {
struct iovec bufs[2] =
{ { .iov_base = bufA, .iov_len = lenA }
, { .iov_base = (void*)(bufB + offB), .iov_len = lenB }
};
struct msghdr msg =
{ .msg_name = NULL
, .msg_namelen = 0
, .msg_iov = bufs
, .msg_iovlen = 2
, .msg_control = NULL
, .msg_controllen = 0
};
return sendmsg(sockfd,&msg,flags);
}
// The first buffer is char* instead of void* because we need
// to apply an offset to it.
ssize_t sendmsg_b
( int sockfd
, char *bufA
, HsInt offA
, size_t lenA
, void *bufB
, size_t lenB
, int flags
) {
struct iovec bufs[2] =
{ { .iov_base = (void*)(bufA + offA), .iov_len = lenA }
, { .iov_base = bufB, .iov_len = lenB }
};
struct msghdr msg =
{ .msg_name = NULL
, .msg_namelen = 0
, .msg_iov = bufs
, .msg_iovlen = 2
, .msg_control = NULL
, .msg_controllen = 0
};
return sendmsg(sockfd,&msg,flags);
}
int recvmmsg_sockaddr_in
( int sockfd
, int *lens // used for output
, struct sockaddr_in *addrs // used for output
, StgMutArrPtrs *arr // used for output
, unsigned int vlen
, int flags
) {
StgClosure **bufsX = arr->payload;
StgArrBytes **bufs = (StgArrBytes**)bufsX;
// TODO: It's probably better to statically pick
// out a maximum size for these. On the C stack,
// the cost of doing this is basically nothing.
// Perhaps 4096 would be a good maximum.
struct mmsghdr msgs[vlen];
struct iovec vecs[vlen];
for(unsigned int i = 0; i < vlen; i++) {
vecs[i].iov_base = (void*)(bufs[i]->payload);
vecs[i].iov_len = (size_t)(bufs[i]->bytes);
// We deliberately leave msg_len unassigned.
msgs[i].msg_hdr.msg_name = addrs + i;
msgs[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
msgs[i].msg_hdr.msg_iov = vecs + i;
msgs[i].msg_hdr.msg_iovlen = 1;
msgs[i].msg_hdr.msg_control = NULL;
msgs[i].msg_hdr.msg_controllen = 0;
msgs[i].msg_hdr.msg_flags = flags;
}
int r = recvmmsg(sockfd,msgs,vlen,flags,NULL);
// If no errors occurred, copy all of the lengths into the
// length buffer. This copy makes me feel a little sad.
// It is the only copy in a wrapper that otherwise is
// able to share buffers perfectly between Haskell and C.
if(r > (-1)) {
for(int i = 0; i < r; i++) {
lens[i] = msgs[i].msg_len;
}
}
return r;
}
int recvmmsg_sockaddr_discard
( int sockfd
, int *lens // used for output
, StgMutArrPtrs *arr // used for output
, unsigned int vlen
, int flags
) {
StgClosure **bufsX = arr->payload;
StgArrBytes **bufs = (StgArrBytes**)bufsX;
struct mmsghdr msgs[vlen];
struct iovec vecs[vlen];
for(unsigned int i = 0; i < vlen; i++) {
vecs[i].iov_base = (void*)(bufs[i]->payload);
vecs[i].iov_len = (size_t)(bufs[i]->bytes);
// We deliberately leave msg_len and msg_flags
// unassigned since they are set by the syscall.
msgs[i].msg_hdr.msg_name = NULL;
msgs[i].msg_hdr.msg_namelen = 0;
msgs[i].msg_hdr.msg_iov = vecs + i;
msgs[i].msg_hdr.msg_iovlen = 1;
msgs[i].msg_hdr.msg_control = NULL;
msgs[i].msg_hdr.msg_controllen = 0;
}
int r = recvmmsg(sockfd,msgs,vlen,flags,NULL);
// TODO: Check msg_flags for MSG_TRUNC. I currently
// do this in haskell, but it is actually easier to
// do here.
// If no errors occurred, copy all of the lengths into the
// length buffer. This copy makes me feel a little sad.
// It is the only copy in a wrapper that otherwise is
// able to share buffers perfectly between Haskell and C.
if(r > (-1)) {
for(int i = 0; i < r; i++) {
lens[i] = msgs[i].msg_len;
}
}
return r;
}
ssize_t mq_send_offset(mqd_t mqdes, const char *msg, HsInt offset, size_t len, unsigned int prio){
return mq_send(mqdes, msg + offset, len, prio);
}
int hs_get_fd_flags(int fd) {
return fcntl(fd,F_GETFD);
}
int hs_get_fl_flags(int fd) {
return fcntl(fd,F_GETFL);
}