From 73f4b962535bc0573f5c8c7159f4b20b0324213f Mon Sep 17 00:00:00 2001 From: Stephanie Wang Date: Thu, 15 Sep 2016 16:28:52 -0700 Subject: [PATCH] Sockets (#3) * Socket methods to be used by an event loop * Git ignore build files * File renames * Some fixes * Fixes * Fixes * Memory leakage fix --- .gitignore | 3 ++ Makefile | 8 +++- event_loop.c | 1 - sockets.c | 111 ++++++++++++++++++++++++++++++++++++++++++++ sockets.h | 10 ++++ test/socket_tests.c | 45 ++++++++++++++++++ 6 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 sockets.c create mode 100644 sockets.h create mode 100644 test/socket_tests.c diff --git a/.gitignore b/.gitignore index 2a07abca4..fff8ef269 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ # Debug files *.dSYM/ *.su + +# Build files +build/* diff --git a/Makefile b/Makefile index fd9da0b97..8ca748e1d 100644 --- a/Makefile +++ b/Makefile @@ -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)/* @@ -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: diff --git a/event_loop.c b/event_loop.c index d7169f5d8..ebc6ebc13 100644 --- a/event_loop.c +++ b/event_loop.c @@ -1,7 +1,6 @@ #include "event_loop.h" #include -#include UT_icd item_icd = {sizeof(event_loop_item), NULL, NULL, NULL}; UT_icd poll_icd = {sizeof(struct pollfd), NULL, NULL, NULL}; diff --git a/sockets.c b/sockets.c new file mode 100644 index 000000000..6fd41e476 --- /dev/null +++ b/sockets.c @@ -0,0 +1,111 @@ +#include "sockets.h" + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/sockets.h b/sockets.h new file mode 100644 index 000000000..7ad0c7141 --- /dev/null +++ b/sockets.h @@ -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 diff --git a/test/socket_tests.c b/test/socket_tests.c new file mode 100644 index 000000000..1fa352246 --- /dev/null +++ b/test/socket_tests.c @@ -0,0 +1,45 @@ +#include "greatest.h" + +#include +#include + +#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(); +}