Skip to content

Commit

Permalink
Sockets (#3)
Browse files Browse the repository at this point in the history
* Socket methods to be used by an event loop

* Git ignore build files

* File renames

* Some fixes

* Fixes

* Fixes

* Memory leakage fix
  • Loading branch information
Stephanie Wang authored and pcmoritz committed Sep 15, 2016
1 parent 7d629d4 commit 73f4b96
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@
# Debug files
*.dSYM/
*.su

# Build files
build/*
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ CFLAGS += -Wmissing-declarations
$(BUILD)/db_tests: hiredis test/db_tests.c thirdparty/greatest.h event_loop.c state/redis.c common.c
$(CC) -o $@ test/db_tests.c event_loop.c state/redis.c common.c thirdparty/hiredis/libhiredis.a $(CFLAGS) -I. -Ithirdparty

$(BUILD)/socket_tests: test/socket_tests.c thirdparty/greatest.h sockets.c
$(CC) -o $@ test/socket_tests.c sockets.c $(CFLAGS) -I. -Ithirdparty

clean:
rm -r $(BUILD)/*

Expand All @@ -18,7 +21,8 @@ redis:
hiredis:
git submodule update --init --recursive -- "thirdparty/hiredis" ; cd thirdparty/hiredis ; make

test: hiredis redis $(BUILD)/db_tests FORCE
./thirdparty/redis-3.2.3/src/redis-server & sleep 1s ; ./build/db_tests
test: hiredis redis $(BUILD)/db_tests $(BUILD)/socket_tests FORCE
./thirdparty/redis-3.2.3/src/redis-server &
sleep 1s ; ./build/db_tests ; ./build/socket_tests

FORCE:
1 change: 0 additions & 1 deletion event_loop.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "event_loop.h"

#include <assert.h>
#include <unistd.h>

UT_icd item_icd = {sizeof(event_loop_item), NULL, NULL, NULL};
UT_icd poll_icd = {sizeof(struct pollfd), NULL, NULL, NULL};
Expand Down
111 changes: 111 additions & 0 deletions sockets.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include "sockets.h"

#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stdio.h>

#include "common.h"

/* Binds to a Unix domain datagram socket at the given
* pathname. Removes any existing file at the pathname. Returns
* a file descriptor for the socket, or -1 if an error
* occurred. */
int bind_ipc_sock(const char *socket_pathname) {
struct sockaddr_un socket_address;
int socket_fd;

socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (socket_fd < 0) {
LOG_ERR("socket() failed for pathname %s.", socket_pathname);
return -1;
}

unlink(socket_pathname);
memset(&socket_address, 0, sizeof(struct sockaddr_un));
socket_address.sun_family = AF_UNIX;
if (strlen(socket_pathname) + 1 > sizeof(socket_address.sun_path)) {
LOG_ERR("Socket pathname is too long.");
return -1;
}
strncpy(socket_address.sun_path, socket_pathname,
strlen(socket_pathname) + 1);

if (bind(socket_fd, (struct sockaddr *) &socket_address,
sizeof(struct sockaddr_un)) != 0) {
LOG_ERR("Bind failed for pathname %s.", socket_pathname);
return -1;
}

return socket_fd;
}

/* Connects to a Unix domain datagram socket at the given
* pathname. Returns a file descriptor for the socket, or -1 if
* an error occurred. */
int connect_ipc_sock(const char *socket_pathname) {
struct sockaddr_un socket_address;
int socket_fd;

socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (socket_fd < 0) {
LOG_ERR("socket() failed for pathname %s.", socket_pathname);
return -1;
}

memset(&socket_address, 0, sizeof(struct sockaddr_un));
socket_address.sun_family = AF_UNIX;
if (strlen(socket_pathname) + 1 > sizeof(socket_address.sun_path)) {
LOG_ERR("Socket pathname is too long.");
return -1;
}
strncpy(socket_address.sun_path, socket_pathname,
strlen(socket_pathname) + 1);

if (connect(socket_fd, (struct sockaddr *) &socket_address,
sizeof(struct sockaddr_un)) != 0) {
LOG_ERR("Connection to socket failed for pathname %s.", socket_pathname);
return -1;
}

return socket_fd;
}

/* Sends a message on the given socket file descriptor. */
void send_ipc_sock(int socket_fd, char *message) {
int length = strlen(message);
int nbytes;
nbytes = send(socket_fd, (char *) &length, sizeof(length), 0);
if (nbytes == -1) {
fprintf(stderr, "Error sending to socket.\n");
return;
}
nbytes = send(socket_fd, (char *) message, length * sizeof(char), 0);
if (nbytes == -1) {
fprintf(stderr, "Error sending to socket.\n");
return;
}
}

/* Receives a message on the given socket file descriptor. Allocates and
* returns a pointer to the message.
* NOTE: Caller must free the message! */
char *recv_ipc_sock(int socket_fd) {
int length;
int nbytes;
nbytes = recv(socket_fd, &length, sizeof(length), 0);
if (nbytes == -1) {
fprintf(stderr, "Error receiving from socket.\n");
return NULL;
}
char *message = malloc((length + 1) * sizeof(char));
nbytes = recv(socket_fd, message, length * sizeof(char), 0);
if (nbytes == -1) {
fprintf(stderr, "Error receiving from socket.\n");
return NULL;
}
message[length] = '\0';
return message;
}
10 changes: 10 additions & 0 deletions sockets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef SOCKETS_H
#define SOCKETS_H

/* Helper functions for socket communication. */
int bind_ipc_sock(const char* socket_pathname);
int connect_ipc_sock(const char* socket_pathname);
void send_ipc_sock(int socket_fd, char* message);
char* recv_ipc_sock(int socket_fd);

#endif
45 changes: 45 additions & 0 deletions test/socket_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "greatest.h"

#include <assert.h>
#include <unistd.h>

#include "sockets.h"

SUITE(event_loop_tests);

TEST ipc_socket_test(void) {
const char* socket_pathname = "test-socket";
int socket_fd = bind_ipc_sock(socket_pathname);
ASSERT(socket_fd >= 0);

char* test_string = "hello world";
pid_t pid = fork();
if (pid == 0) {
close(socket_fd);
socket_fd = connect_ipc_sock(socket_pathname);
ASSERT(socket_fd >= 0);
send_ipc_sock(socket_fd, test_string);
close(socket_fd);
} else {
char* message = recv_ipc_sock(socket_fd);
ASSERT(message != NULL);
ASSERT_STR_EQ(test_string, message);
free(message);
close(socket_fd);
unlink(socket_pathname);
}

PASS();
}

SUITE(event_loop_tests) {
RUN_TEST(ipc_socket_test);
}

GREATEST_MAIN_DEFS();

int main(int argc, char** argv) {
GREATEST_MAIN_BEGIN();
RUN_SUITE(event_loop_tests);
GREATEST_MAIN_END();
}

0 comments on commit 73f4b96

Please sign in to comment.