#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stdbool.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
static bool pass = true;
typedef struct to_test {
char *name;
int option;
time_t sec;
suseconds_t usec;
} to_test_t;
static to_test_t tests[] = {
{
.name = "Set 5s receive",
.option = SO_RCVTIMEO,
.sec = 5,
.usec = 0
}, {
.name = "Set 5s send",
.option = SO_SNDTIMEO,
.sec = 5,
.usec = 0
}, {
.name = "Set 15410s receive",
.option = SO_RCVTIMEO,
.sec = 15410,
.usec = 0
}, {
.name = "Set 15410s send",
.option = SO_SNDTIMEO,
.sec = 15410,
.usec = 0
}, {
.name = "Set 0s receive",
.option = SO_RCVTIMEO,
.sec = 0,
.usec = 0
}, {
.name = "Set 0s send",
.option = SO_SNDTIMEO,
.sec = 0,
.usec = 0
}, {
.name = "Set 5.5s receive",
.option = SO_RCVTIMEO,
.sec = 5,
.usec = MICROSEC / 2,
}, {
.name = "Set 5.5s send",
.option = SO_SNDTIMEO,
.sec = 5,
.usec = MICROSEC / 2,
}
};
static int
server(const char *sockpath)
{
struct sockaddr_un addr;
int sock;
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock == -1)
err(EXIT_FAILURE, "failed to create socket");
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
err(EXIT_FAILURE, "bind failed");
if (listen(sock, 0) == -1)
err(EXIT_FAILURE, "listen failed");
return (sock);
}
static int
client(const char *sockpath)
{
struct sockaddr_un addr;
int sock;
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock == -1)
err(EXIT_FAILURE, "failed to create socket");
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
if (connect(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
err(EXIT_FAILURE, "could not connect to server socket");
return (sock);
}
static void __PRINTFLIKE(2)
fail(const to_test_t *t, const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "[FAIL] %s: ", t->name);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
pass = false;
}
int
main(int argc, const char **argv)
{
char sockpath[] = "/tmp/to.testsock.XXXXXX";
int sfd, cfd;
if (mktemp(sockpath) == NULL)
err(EXIT_FAILURE, "Failed to make temporary socket path");
sfd = server(sockpath);
cfd = client(sockpath);
for (uint_t i = 0; i < ARRAY_SIZE(tests); i++) {
const to_test_t *t = &tests[i];
struct timeval tv = { 0 };
socklen_t optlen;
tv.tv_sec = t->sec;
tv.tv_usec = t->usec;
optlen = sizeof (tv);
if (setsockopt(cfd, SOL_SOCKET, t->option, &tv, optlen) != 0) {
fail(t, "setsockopt error: %s", strerror(errno));
pass = false;
continue;
}
bzero(&tv, sizeof (tv));
if (getsockopt(cfd, SOL_SOCKET, t->option, &tv, &optlen) != 0) {
fail(t, "getsockopt error: %s", strerror(errno));
pass = false;
continue;
}
if (optlen != sizeof (tv)) {
fail(t,
"getsockopt returned incorrect length: %ld"
" vs. %zd", (long)optlen, sizeof (tv));
continue;
}
if (tv.tv_sec != t->sec) {
fail(t, "returned tv_sec value mismatch: %ld "
"vs. expected %ld", tv.tv_sec, t->sec);
continue;
}
if (tv.tv_usec != t->usec) {
fail(t, "returned tv_usec value mismatch: %ld "
"vs. expected %ld", tv.tv_usec, t->usec);
continue;
}
printf("[PASS] %s\n", t->name);
}
close(cfd);
close(sfd);
unlink(sockpath);
return (pass ? 0 : EXIT_FAILURE);
}