AtlatestRepositorysigil-websocket
sigil-websocket / tree / test / integrationshort-socket-writes.c
1
/* Linux test instrument: force short writes and EAGAIN on nonblocking2
* stream sockets. Run ONLY in an isolated client via LD_PRELOAD.3
* cc -shared -fPIC -o short-socket-writes.so short-socket-writes.c -ldl4
* SIGIL_SHORT_WRITE_LOG optionally records counts, never payload bytes. */5
#define _GNU_SOURCE6
#include <dlfcn.h>7
#include <sys/socket.h>8
#include <fcntl.h>9
#include <errno.h>10
#include <unistd.h>11
#include <stdio.h>12
#include <stdlib.h>14
static unsigned short_writes, would_block;15
static int constrain(int fd, size_t *length) {16
int type;17
socklen_t size = sizeof(type);18
if (*length <= 4096 || !(fcntl(fd, F_GETFL) & O_NONBLOCK) ||19
getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &size) != 0 ||20
type != SOCK_STREAM) return 0;21
if (short_writes > would_block) {22
++would_block;23
errno = EAGAIN;24
return -1;25
}26
++short_writes;27
*length = 4096;28
return 0;29
}31
ssize_t send(int fd, const void *bytes, size_t length, int flags) {32
static ssize_t (*native_send)(int, const void *, size_t, int);33
if (!native_send) native_send = dlsym(RTLD_NEXT, "send");34
if (constrain(fd, &length) < 0) return -1;35
return native_send(fd, bytes, length, flags);36
}38
ssize_t write(int fd, const void *bytes, size_t length) {39
static ssize_t (*native_write)(int, const void *, size_t);40
if (!native_write) native_write = dlsym(RTLD_NEXT, "write");41
if (constrain(fd, &length) < 0) return -1;42
return native_write(fd, bytes, length);43
}45
__attribute__((destructor)) static void record_counts(void) {46
const char *path = getenv("SIGIL_SHORT_WRITE_LOG");47
if (!path || (!short_writes && !would_block)) return;48
int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0600);49
if (fd < 0) return;50
char line[128];51
int length = snprintf(line, sizeof(line), "%ld %u %u\n",52
(long)getpid(), short_writes, would_block);53
if (length > 0 && (size_t)length < sizeof(line)) write(fd, line, length);54
close(fd);55
}