voyeur-0.1.0.0: libvoyeur/src/net.c
#ifdef __linux__
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#ifdef __linux__
#include <bsd/bsd.h>
#endif
#include "net.h"
#include "util.h"
int voyeur_create_server_socket(struct sockaddr_un* sockinfo)
{
// Configure a unix domain socket at a temporary path.
char sockdir[] = "/tmp/libvoyeur-XXXXXXXXX";
mkdtemp(sockdir);
memset(sockinfo, 0, sizeof(struct sockaddr_un));
sockinfo->sun_family = AF_UNIX;
strlcpy(sockinfo->sun_path, sockdir, sizeof(sockinfo->sun_path));
strlcat(sockinfo->sun_path, "/socket", sizeof(sockinfo->sun_path));
unlink(sockinfo->sun_path);
// Start the server.
int server_sock = socket(AF_UNIX, SOCK_STREAM, 0);
RETURN_ERROR_ON_FAIL(bind, server_sock, (struct sockaddr*) sockinfo,
sizeof(struct sockaddr_un));
RETURN_ERROR_ON_FAIL(listen, server_sock, 200);
//chmod(sockinfo->sun_path, 0777);
//fchmod(server_sock, 0777);
fcntl(server_sock, F_SETFD, FD_CLOEXEC);
return server_sock;
}
#define CONNECT_RETRIES 20
#define CONNECT_WAIT_MS 50
#define CONNECT_DEVIATION_MS 20
int voyeur_create_client_socket(const char* sockpath)
{
struct sockaddr_un sockinfo;
memset(&sockinfo, 0, sizeof(struct sockaddr_un));
sockinfo.sun_family = AF_UNIX;
strlcpy(sockinfo.sun_path, sockpath, sizeof(sockinfo.sun_path));
// Connect to the server.
int client_sock = socket(AF_UNIX, SOCK_STREAM, 0);
int set = 1;
#ifdef __APPLE__
setsockopt(client_sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
#endif
unsigned try = 0;
int connect_status = connect(client_sock,
(struct sockaddr*) &sockinfo,
sizeof(struct sockaddr_un));
while (connect_status < 0 && ++try < CONNECT_RETRIES) {
usleep(((CONNECT_WAIT_MS - CONNECT_DEVIATION_MS / 2) +
arc4random_uniform(CONNECT_DEVIATION_MS)) * 1000);
connect_status = connect(client_sock,
(struct sockaddr*) &sockinfo,
sizeof(struct sockaddr_un));
}
fcntl(client_sock, F_SETFD, FD_CLOEXEC);
return client_sock;
}
void voyeur_close_socket(int fd)
{
while (close(fd) < 0) {
if (errno != EINTR) {
// We really only want to keep spinning for EINTR.
break;
}
}
}
#ifdef __linux__
#define SEND_OPTS MSG_NOSIGNAL
#else
#define SEND_OPTS 0
#endif
static int do_write(int fd, void* buf, size_t buf_size)
{
ssize_t total_out = 0;
while (total_out < (ssize_t) buf_size) {
ssize_t out = send(fd,
buf + total_out,
buf_size - total_out,
SEND_OPTS);
// Handle errors.
if (out < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
continue;
} else {
return -1;
}
}
total_out += out;
}
return 0;
}
static int do_read(int fd, void* buf, size_t buf_size)
{
ssize_t total_in = 0;
while (total_in < (ssize_t) buf_size) {
ssize_t in = read(fd,
buf + total_in,
buf_size - total_in);
// Handle errors.
if (in < 0) {
if (errno == EAGAIN || errno == EINTR) {
continue;
} else {
return -1;
}
}
// Handle EOF.
if (in == 0) {
return -1;
}
total_in += in;
}
return 0;
}
int voyeur_write_msg_type(int fd, voyeur_msg_type val)
{
return do_write(fd, (void*) &val, sizeof(voyeur_msg_type));
}
int voyeur_read_msg_type(int fd, voyeur_msg_type* val)
{
return do_read(fd, (void*) val, sizeof(voyeur_msg_type));
}
int voyeur_write_event_type(int fd, voyeur_event_type val)
{
return do_write(fd, (void*) &val, sizeof(voyeur_event_type));
}
int voyeur_read_event_type(int fd, voyeur_event_type* val)
{
return do_read(fd, (void*) val, sizeof(voyeur_event_type));
}
int voyeur_write_byte(int fd, char val)
{
return do_write(fd, (void*) &val, sizeof(char));
}
int voyeur_read_byte(int fd, char* val)
{
return do_read(fd, (void*) val, sizeof(char));
}
int voyeur_write_int(int fd, int val)
{
return do_write(fd, (void*) &val, sizeof(int));
}
int voyeur_read_int(int fd, int* val)
{
return do_read(fd, (void*) val, sizeof(int));
}
int voyeur_write_size(int fd, size_t val)
{
return do_write(fd, (void*) &val, sizeof(size_t));
}
int voyeur_read_size(int fd, size_t* val)
{
return do_read(fd, (void*) val, sizeof(size_t));
}
int voyeur_write_pid(int fd, pid_t val)
{
return do_write(fd, (void*) &val, sizeof(pid_t));
}
int voyeur_read_pid(int fd, pid_t* val)
{
return do_read(fd, (void*) val, sizeof(pid_t));
}
int voyeur_write_string(int fd, const char* val, size_t len)
{
if (val == NULL) {
len = 0;
} else if (len == 0) {
len = strnlen(val, VOYEUR_MAX_STRLEN);
}
if (voyeur_write_size(fd, len) < 0) {
return -1;
}
return do_write(fd, (void*) val, len);
}
int voyeur_read_string(int fd, char** val, size_t maxlen)
{
size_t len;
if (voyeur_read_size(fd, &len) < 0) {
return -1;
}
if (maxlen == 0) {
*val = malloc(len + 1);
} else if (maxlen <= len) {
SHOULD_NOT_REACH("libvoyeur: string buffer of size %zu "
"is too small for string of length %zu\n",
maxlen,
len);
return -1;
}
if (do_read(fd, (void*) *val, len) < 0) {
if (maxlen == 0) {
free(*val);
}
return -1;
}
(*val)[len] = '\0';
return 0;
}