18#include <sys/syscall.h>
22#define CHECK(expression) \
24 if (!(expression)) { \
25 fprintf(stderr, "RECVMMSG-CONTRACT: FAIL line=%d errno=%d\n", __LINE__, errno); \
30static char socket_paths[16][
sizeof(((
struct sockaddr_un*)0)->sun_path)];
31static size_t socket_path_count;
33static void cleanup_paths(
void) {
34 for (
size_t i = 0; i < socket_path_count; ++i)
35 unlink(socket_paths[i]);
38static int datagram_pair(
int sockets[2]) {
39 struct sockaddr_un addresses[2] = {{.sun_family = AF_UNIX}, {.sun_family = AF_UNIX}};
40 sockets[0] = sockets[1] = -1;
41 for (
int i = 0; i < 2; ++i) {
42 if (socket_path_count ==
sizeof(socket_paths) /
sizeof(socket_paths[0])) {
46 char* path = socket_paths[socket_path_count];
47 snprintf(path,
sizeof(socket_paths[0]),
"/tmp/recvmmsg-%ld-%zu", (
long)getpid(),
50 memcpy(addresses[i].sun_path, path, strlen(path) + 1);
51 sockets[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
52 if (sockets[i] < 0 || bind(sockets[i], (
struct sockaddr*)&addresses[i],
53 offsetof(
struct sockaddr_un, sun_path) + strlen(path) + 1))
56 for (
int i = 0; i < 2; ++i) {
57 const struct sockaddr_un* peer = &addresses[1 - i];
58 if (connect(sockets[i], (
const struct sockaddr*)peer,
59 offsetof(
struct sockaddr_un, sun_path) + strlen(peer->sun_path) + 1))
64 const int error = errno;
74static volatile sig_atomic_t signalled;
75static void handler(
int signal) {
80static void initialise(
struct mmsghdr* messages,
struct iovec* vectors,
char data[][16],
size_t n) {
81 memset(messages, 0, n *
sizeof(*messages));
82 for (
size_t i = 0; i < n; ++i) {
83 vectors[i].iov_base = data[i];
84 vectors[i].iov_len = 16;
85 messages[i].msg_hdr.msg_iov = &vectors[i];
86 messages[i].msg_hdr.msg_iovlen = 1;
90static int pendingError(
int fd,
int expected) {
92 socklen_t length =
sizeof(error);
93 return getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &length) == 0 && error == expected;
96static int datagrams(
void) {
98 CHECK(datagram_pair(sockets) == 0);
99 struct mmsghdr messages[3];
100 struct iovec vectors[3];
102 initialise(messages, vectors, data, 3);
103 CHECK(write(sockets[0],
"one", 3) == 3 && write(sockets[0],
"two", 3) == 3);
104 CHECK(recvmmsg(sockets[1], messages, 3, MSG_WAITFORONE, NULL) == 2);
105 CHECK(messages[0].msg_len == 3 && messages[1].msg_len == 3 && !memcmp(data[0],
"one", 3) &&
106 !memcmp(data[1],
"two", 3));
107 CHECK(!(fcntl(sockets[1], F_GETFL) & O_NONBLOCK));
108 CHECK(recvmmsg(sockets[1], messages, 1, MSG_DONTWAIT, NULL) == -1 && errno == EAGAIN);
109 CHECK(syscall(SYS_recvmmsg, sockets[1], NULL, 0, 0, NULL) == 0);
110 CHECK(syscall(SYS_recvmmsg, -1, NULL, 0, 0, NULL) == -1 && errno == EBADF);
112 initialise(messages, vectors, data, 3);
113 CHECK(write(sockets[0],
"before", 6) == 6 && write(sockets[0],
"after", 5) == 5);
114 vectors[1].iov_base = (
void*)1;
115 CHECK(recvmmsg(sockets[1], messages, 2, MSG_DONTWAIT, NULL) == 1);
116 CHECK(messages[0].msg_len == 6 && !memcmp(data[0],
"before", 6));
117 CHECK(pendingError(sockets[1], EFAULT));
118 initialise(messages, vectors, data, 3);
119 CHECK(recvmmsg(sockets[1], messages, 1, MSG_DONTWAIT, NULL) == 1);
120 CHECK(messages[0].msg_len == 5 && !memcmp(data[0],
"after", 5));
122 CHECK(write(sockets[0],
"abcdef", 6) == 6);
123 initialise(messages, vectors, data, 3);
124 vectors[0].iov_len = 2;
125 CHECK(recvmmsg(sockets[1], messages, 1, MSG_TRUNC, NULL) == 1);
126 CHECK(messages[0].msg_len == 6 && (messages[0].msg_hdr.msg_flags & MSG_TRUNC) &&
127 !memcmp(data[0],
"ab", 2));
128 CHECK(write(sockets[0],
"", 0) == 0);
129 initialise(messages, vectors, data, 3);
130 CHECK(recvmmsg(sockets[1], messages, 1, MSG_DONTWAIT, NULL) == 1 && messages[0].msg_len == 0);
131 CHECK(fcntl(sockets[1], F_SETFL, O_NONBLOCK) == 0);
132 CHECK(recvmmsg(sockets[1], messages, 1, 0, NULL) == -1 && errno == EAGAIN);
133 CHECK(close(sockets[0]) == 0 && close(sockets[1]) == 0);
137static int deadlines(
void) {
139 CHECK(datagram_pair(sockets) == 0);
140 struct mmsghdr messages[2];
141 struct iovec vectors[2];
143 initialise(messages, vectors, data, 2);
144 struct timespec timeout = {0, 30000000}, before, after;
145 CHECK(clock_gettime(CLOCK_MONOTONIC, &before) == 0);
146 CHECK(recvmmsg(sockets[1], messages, 2, 0, &timeout) == 0);
147 CHECK(clock_gettime(CLOCK_MONOTONIC, &after) == 0);
148 int64_t elapsed = (after.tv_sec - before.tv_sec) * 1000000000LL + after.tv_nsec - before.tv_nsec;
149 CHECK(elapsed >= 20000000 && elapsed < 2000000000LL);
150 CHECK(write(sockets[0],
"partial", 7) == 7);
151 timeout = (
struct timespec){0, 30000000};
152 CHECK(recvmmsg(sockets[1], messages, 2, 0, &timeout) == 1);
153 CHECK(messages[0].msg_len == 7 && !memcmp(data[0],
"partial", 7));
154 CHECK(timeout.tv_sec == 0 && timeout.tv_nsec == 0);
155 timeout = (
struct timespec){-1, 0};
156 CHECK(recvmmsg(sockets[1], messages, 1, 0, &timeout) == -1 && errno == EINVAL);
157 timeout = (
struct timespec){0, 1000000000};
158 CHECK(recvmmsg(sockets[1], messages, 1, 0, &timeout) == -1 && errno == EINVAL);
159 CHECK(syscall(SYS_recvmmsg, sockets[1], messages, 1, 0, (
void*)1) == -1 && errno == EFAULT);
160 CHECK(syscall(SYS_recvmmsg, sockets[1], (
void*)1, 1, MSG_DONTWAIT, NULL) == -1 &&
162 CHECK(close(sockets[0]) == 0 && close(sockets[1]) == 0);
166static int signals(
void) {
167 struct sigaction action = {.sa_handler = handler};
168 sigemptyset(&action.sa_mask);
169 CHECK(sigaction(SIGUSR1, &action, NULL) == 0);
171 CHECK(datagram_pair(sockets) == 0);
172 struct mmsghdr messages[2];
173 struct iovec vectors[2];
175 for (
int partial = 0; partial <= 1; ++partial) {
176 initialise(messages, vectors, data, 2);
179 CHECK(write(sockets[0],
"saved", 5) == 5);
180 pid_t child = fork();
184 _exit(kill(getppid(), SIGUSR1) ? 2 : 0);
186 struct timespec timeout = {2, 0};
187 const int result = recvmmsg(sockets[1], messages, 2, 0, &timeout);
188 const int error = errno;
189 CHECK(signalled && (partial ? result == 1 : result == -1 && error == EINTR));
191 CHECK(messages[0].msg_len == 5 && !memcmp(data[0],
"saved", 5));
192 CHECK(pendingError(sockets[1], EINTR));
195 CHECK(waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0);
197 action.sa_flags = SA_RESTART;
198 CHECK(sigaction(SIGUSR1, &action, NULL) == 0);
199 for (
int timed = 0; timed <= 1; ++timed) {
200 initialise(messages, vectors, data, 2);
202 pid_t child = fork();
206 if (kill(getppid(), SIGUSR1))
209 _exit(write(sockets[0],
"restart", 7) == 7 ? 0 : 3);
211 struct timespec timeout = {2, 0};
212 const int result = recvmmsg(sockets[1], messages, 1, 0, timed ? &timeout : NULL);
213 const int error = errno;
214 CHECK(signalled && (timed ? result == -1 && error == EINTR : result == 1));
216 CHECK(waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0);
218 CHECK(timeout.tv_sec == 2 && timeout.tv_nsec == 0);
219 CHECK(recvmmsg(sockets[1], messages, 1, MSG_DONTWAIT, NULL) == 1);
221 CHECK(messages[0].msg_len == 7 && !memcmp(data[0],
"restart", 7));
223 CHECK(close(sockets[0]) == 0 && close(sockets[1]) == 0);
233static void* receive_thread(
void* argument) {
235 struct iovec vector = {call->payload,
sizeof(call->payload)};
236 struct mmsghdr messages[2] = {{.msg_hdr = {.msg_iov = &vector, .msg_iovlen = 1}}, {0}};
237 struct timespec timeout = {2, 0};
238 call->result = recvmmsg(call->fd, messages, 2, MSG_WAITFORONE, &timeout);
242static int descriptor_reuse(
void) {
243 int original[2], replacement[2];
244 CHECK(datagram_pair(original) == 0);
245 CHECK(datagram_pair(replacement) == 0);
246 int keep = dup(original[1]);
250 CHECK(pthread_create(&thread, NULL, receive_thread, &call) == 0);
252 CHECK(dup2(replacement[1], original[1]) == original[1]);
253 CHECK(write(replacement[0],
"new", 3) == 3);
254 CHECK(write(original[0],
"old", 3) == 3);
255 CHECK(pthread_join(thread, NULL) == 0);
256 CHECK(call.result == 1 && !memcmp(call.payload,
"old", 3));
258 CHECK(recv(original[1], payload,
sizeof(payload), MSG_DONTWAIT) == 3 &&
259 !memcmp(payload,
"new", 3));
260 CHECK(!(fcntl(keep, F_GETFL) & O_NONBLOCK));
261 CHECK(close(keep) == 0 && close(original[0]) == 0 && close(original[1]) == 0);
262 CHECK(close(replacement[0]) == 0 && close(replacement[1]) == 0);
272static void* poll_error(
void* argument) {
274 struct pollfd descriptor = {.fd = call->fd};
275 call->result = poll(&descriptor, 1, 2000);
276 call->events = descriptor.revents;
280static int deferred_readiness(
void) {
282 CHECK(datagram_pair(sockets) == 0);
283 int epoll = epoll_create1(EPOLL_CLOEXEC);
285 struct epoll_event event = {.events = EPOLLET, .data.fd = sockets[1]};
286 CHECK(epoll_ctl(epoll, EPOLL_CTL_ADD, sockets[1], &event) == 0);
287 for (
int iteration = 0; iteration < 2; ++iteration) {
290 CHECK(pthread_create(&
waiter, NULL, poll_error, &call) == 0);
292 struct mmsghdr messages[2];
293 struct iovec vectors[2];
295 initialise(messages, vectors, data, 2);
296 vectors[1].iov_base = (
void*)1;
297 CHECK(write(sockets[0],
"error", 5) == 5);
298 CHECK(recvmmsg(sockets[1], messages, 2, MSG_DONTWAIT, NULL) == 1);
299 CHECK(pthread_join(
waiter, NULL) == 0);
300 CHECK(call.result == 1 && (call.events & POLLERR));
301 CHECK(epoll_wait(epoll, &event, 1, 0) == 1 && (event.events & EPOLLERR));
302 CHECK(epoll_wait(epoll, &event, 1, 0) == 0);
303 struct pollfd descriptor = {.fd = sockets[1]};
304 CHECK(poll(&descriptor, 1, 0) == 1 && (descriptor.revents & POLLERR));
305 CHECK(pendingError(sockets[1], EFAULT));
306 CHECK(poll(&descriptor, 1, 0) == 0 && epoll_wait(epoll, &event, 1, 0) == 0);
308 CHECK(close(epoll) == 0 && close(sockets[0]) == 0 && close(sockets[1]) == 0);
312static int rights(
void) {
313 int sockets[2], pipefd[2];
314 CHECK(datagram_pair(sockets) == 0 && pipe(pipefd) == 0);
316 struct cmsghdr alignment;
317 char bytes[CMSG_SPACE(
sizeof(
int))];
319 memset(&control, 0,
sizeof(control));
321 struct iovec vector = {&payload, 1};
322 struct msghdr
message = {.msg_iov = &vector,
324 .msg_control = control.bytes,
325 .msg_controllen =
sizeof(control)};
326 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
327 header->cmsg_level = SOL_SOCKET;
328 header->cmsg_type = SCM_RIGHTS;
329 header->cmsg_len = CMSG_LEN(
sizeof(
int));
330 memcpy(CMSG_DATA(header), &pipefd[0],
sizeof(
int));
331 CHECK(sendmsg(sockets[0], &
message, 0) == 1 && sendmsg(sockets[0], &
message, 0) == 1);
333 struct mmsghdr messages[2];
334 struct iovec vectors[2];
337 struct cmsghdr alignment;
338 char bytes[CMSG_SPACE(
sizeof(
int))];
340 initialise(messages, vectors, data, 2);
341 for (
int i = 0; i < 2; ++i) {
342 messages[i].msg_hdr.msg_control = received[i].bytes;
343 messages[i].msg_hdr.msg_controllen =
sizeof(received[i]);
345 CHECK(recvmmsg(sockets[1], messages, 2, MSG_CMSG_CLOEXEC, NULL) == 2);
346 for (
int i = 0; i < 2; ++i) {
347 header = CMSG_FIRSTHDR(&messages[i].msg_hdr);
348 CHECK(header && header->cmsg_level == SOL_SOCKET && header->cmsg_type == SCM_RIGHTS);
350 memcpy(&fd, CMSG_DATA(header),
sizeof(fd));
351 CHECK(fcntl(fd, F_GETFD) & FD_CLOEXEC);
352 CHECK(write(pipefd[1],
"p", 1) == 1 && read(fd, &payload, 1) == 1 && payload ==
'p');
353 CHECK(close(fd) == 0);
355 CHECK(close(sockets[0]) == 0 && close(sockets[1]) == 0);
356 CHECK(close(pipefd[0]) == 0 && close(pipefd[1]) == 0);
361 setvbuf(stdout, NULL, _IONBF, 0);
363 CHECK(atexit(cleanup_paths) == 0);
364 puts(
"RECVMMSG-CONTRACT: datagrams");
365 CHECK(datagrams() == 0);
366 puts(
"RECVMMSG-CONTRACT: deadlines");
367 CHECK(deadlines() == 0);
368 puts(
"RECVMMSG-CONTRACT: signals");
369 CHECK(signals() == 0);
370 puts(
"RECVMMSG-CONTRACT: rights");
371 CHECK(rights() == 0);
372 puts(
"RECVMMSG-CONTRACT: descriptor_reuse");
373 CHECK(descriptor_reuse() == 0);
374 puts(
"RECVMMSG-CONTRACT: deferred_readiness");
375 CHECK(deferred_readiness() == 0);
376 puts(
"RECVMMSG-CONTRACT: PASS");