summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2019-07-29 21:03:01 -0400
committerRich Felker <dalias@aerifal.cx>2019-07-29 21:03:01 -0400
commit6a4a1691a0653bd51a30c2b8ac19448b7ebac796 (patch)
tree1a004286f76c583abafa03b2b2f9623a3f4639d1 /src
parent558c01338b0b635632e70af6ec8a484ca70b0328 (diff)
downloadmusl-6a4a1691a0653bd51a30c2b8ac19448b7ebac796.tar.gz
recvmmsg: add time64 syscall support, decouple 32-bit time_t
the time64 syscall is used only if the timeout does not fit in 32 bits. after preprocessing, the code is unchanged on 64-bit archs. for 32-bit archs, the timeout now goes through an intermediate copy, meaning that the caller does not get back the updated timeout. this is based on my reading of the documentation, which does not document the updating as a contract you can rely on, and mentions that the whole recvmmsg timeout mechanism is buggy and unlikely to be useful. if it turns out that there's interest in making the remaining time officially available to callers, such functionality could be added back later.
Diffstat (limited to 'src')
-rw-r--r--src/network/recvmmsg.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/src/network/recvmmsg.c b/src/network/recvmmsg.c
index 58b1b2f6..1dc67932 100644
--- a/src/network/recvmmsg.c
+++ b/src/network/recvmmsg.c
@@ -1,8 +1,13 @@
#define _GNU_SOURCE
#include <sys/socket.h>
#include <limits.h>
+#include <errno.h>
+#include <time.h>
#include "syscall.h"
+#define IS32BIT(x) !((x)+0x80000000ULL>>32)
+#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63))
+
int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec *timeout)
{
#if LONG_MAX > INT_MAX
@@ -11,5 +16,18 @@ int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int fla
for (i = vlen; i; i--, mh++)
mh->msg_hdr.__pad1 = mh->msg_hdr.__pad2 = 0;
#endif
+#ifdef SYS_recvmmsg_time64
+ time_t s = timeout ? timeout->tv_sec : 0;
+ long ns = timeout ? timeout->tv_nsec : 0;
+ int r = -ENOSYS;
+ if (SYS_recvmmsg == SYS_recvmmsg_time64 || !IS32BIT(s))
+ r = __syscall_cp(SYS_recvmmsg_time64, fd, msgvec, vlen, flags,
+ ((long long[]){s, ns}));
+ if (SYS_recvmmsg == SYS_recvmmsg_time64 || r!=-ENOSYS)
+ return __syscall_ret(r);
+ return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags,
+ timeout ? ((long[]){CLAMP(s), ns}) : 0);
+#else
return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags, timeout);
+#endif
}