From 70bb065fbecc8f382ad9f9421488f7392fe16271 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 14 Jan 2026 22:03:11 +0100 Subject: [PATCH] initial --- .gitignore | 8 + CMakeLists.txt | 70 ++++++++ README.md | 0 include/common/dynarr.h | 57 +++++++ include/common/numgen.h | 12 ++ include/common/task/task.h | 46 +++++ include/common/tcpd/tcpclient.h | 27 +++ include/common/tcpd/tcpserver.h | 54 ++++++ include/server/task/taskqueue.h | 17 ++ src/client/main.c | 6 + src/common/dynarr.c | 164 ++++++++++++++++++ src/common/numgen.c | 17 ++ src/common/task/task.c | 33 ++++ src/common/tcpd/tcpserver.c | 292 ++++++++++++++++++++++++++++++++ src/server/main.c | 39 +++++ src/server/task/taskqueue.c | 93 ++++++++++ 16 files changed, 935 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 include/common/dynarr.h create mode 100644 include/common/numgen.h create mode 100644 include/common/task/task.h create mode 100644 include/common/tcpd/tcpclient.h create mode 100644 include/common/tcpd/tcpserver.h create mode 100644 include/server/task/taskqueue.h create mode 100644 src/client/main.c create mode 100644 src/common/dynarr.c create mode 100644 src/common/numgen.c create mode 100644 src/common/task/task.c create mode 100644 src/common/tcpd/tcpserver.c create mode 100644 src/server/main.c create mode 100644 src/server/task/taskqueue.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..493ec6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +CMakeCache.txt +cmake_install.cmake +Makefile +svrtest +main +CMakeFiles/ +.vscode/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..14746b3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.16) + +project(miniboinc + VERSION 0.1.0 + LANGUAGES C +) + +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +# --------------------------------------------------------- +# Output directories +# --------------------------------------------------------- +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib) +endforeach() + +# Common +file(GLOB_RECURSE COMMON_SRC CONFIGURE_DEPENDS src/common/*.c) +add_library(common STATIC ${COMMON_SRC}) +target_link_libraries(common PUBLIC) +target_compile_options(common PRIVATE + -Wall + -Wextra + -Wpedantic + -lpthread +) +target_include_directories(common PUBLIC + ${PROJECT_SOURCE_DIR}/include +) + +# Client +file(GLOB_RECURSE CLIENT_SRC CONFIGURE_DEPENDS src/client/*.c) +add_executable(client ${CLIENT_SRC}) +target_link_libraries(client PRIVATE common) +target_include_directories(client PRIVATE + ${PROJECT_SOURCE_DIR}/include +) +target_compile_options(client PRIVATE + -Wall + -Wextra + -Wpedantic + -lpthread +) +set_target_properties(client PROPERTIES OUTPUT_NAME "miniboinc_client") + +# Server +file(GLOB_RECURSE SERVER_SRC CONFIGURE_DEPENDS src/server/*.c) +add_executable(server ${SERVER_SRC}) +target_link_libraries(server PRIVATE common) +target_include_directories(server PRIVATE + ${PROJECT_SOURCE_DIR}/include +) +target_compile_options(server PRIVATE + -Wall + -Wextra + -Wpedantic + -lpthread + -g +) +target_compile_definitions(server PRIVATE) +set_target_properties(server PROPERTIES OUTPUT_NAME "miniboinc_server") diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/include/common/dynarr.h b/include/common/dynarr.h new file mode 100644 index 0000000..098b87d --- /dev/null +++ b/include/common/dynarr.h @@ -0,0 +1,57 @@ +#ifndef DYNARR_H +#define DYNARR_H + +#include +#include +#include + +#define DYNARR_MAX_CAPACITY ((size_t)0x7FFFFFFF) + +typedef struct { + size_t size; + size_t elemSize; + size_t capacity; + void* data; +} DynArr; + +// Do not use; Use DYNARR_CREATE macro instead. +DynArr* DynArr_create(size_t elemSize, size_t capacity); + +// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead. +void DynArr_reserve(DynArr* p, size_t n); + +// Push data into a new block at the end of the array +void* DynArr_push_back(DynArr* p, void* value); + +// Remove the last block in the array. +void DynArr_pop_back(DynArr* p); + +// Remove first block from array. +void DynArr_pop_front(DynArr* p); + +// Remove index from array. This moves all blocks after the index block. +void DynArr_remove(DynArr* p, size_t index); + +// Erase the array. This will not free unused blocks. +void DynArr_erase(DynArr* p); + +// Prune and free unused blocks. If pruning to zero, ensure to reserve after. +void DynArr_prune(DynArr* p); + +// Get a pointer to a block by index +void* DynArr_at(DynArr* p, size_t index); + +// Get the index by block pointer +size_t DynArr_at_ptr(DynArr* p, void* ptr); + +// Get size +size_t DynArr_size(DynArr* p); + +// Get capacity +size_t DynArr_capacity(DynArr* p); + +void DynArr_destroy(DynArr* p); + +#define DYNARR_CREATE(T, initialCapacity) DynArr_create(sizeof(T), initialCapacity) + +#endif \ No newline at end of file diff --git a/include/common/numgen.h b/include/common/numgen.h new file mode 100644 index 0000000..3e74955 --- /dev/null +++ b/include/common/numgen.h @@ -0,0 +1,12 @@ +#ifndef NUMGEN_H +#define NUMGEN_H + +#include +#include +#include +#include + +unsigned char random_byte(void); +uint32_t random_four_byte(void); + +#endif \ No newline at end of file diff --git a/include/common/task/task.h b/include/common/task/task.h new file mode 100644 index 0000000..b3e55ff --- /dev/null +++ b/include/common/task/task.h @@ -0,0 +1,46 @@ +#ifndef TASK_H +#define TASK_H + +#include +#include +#include +#include +#include + +typedef enum { + TASK_NONE, + TASK_PENDING, + TASK_ASSIGNED, + TASK_DONE, + TASK_FAILED +} task_state_t; + +extern int TASK_ERR_CODE; + +typedef enum { + TASK_ERR_NONE, + TASK_ERR_INVPTR, + TASK_ERR_INVARGS, + TASK_ERR_INVQUEUE, + TASK_ERR_INVIDX, + TASK_ERR_INVTASK +} task_err; + +typedef struct { + char name[32]; // string + DynArr* data; +} task_arg_t; + +typedef struct { + uint32_t taskId; + char binary[64]; + DynArr* args; + task_state_t state; + uint32_t assigned_to; + time_t assigned_at; +} task_t; + +void Task_Create(task_t* tsk); +void Task_DestroyArgs(task_t* task); + +#endif \ No newline at end of file diff --git a/include/common/tcpd/tcpclient.h b/include/common/tcpd/tcpclient.h new file mode 100644 index 0000000..7bf1331 --- /dev/null +++ b/include/common/tcpd/tcpclient.h @@ -0,0 +1,27 @@ +#ifndef TCPCLIENT_H +#define TCPCLIENT_H + +#include +#include +#include +#include +#include +#include + +#define MTU 1500 + +struct TcpClient { + int clientFd; + struct sockaddr_in clientAddr; + uint32_t clientId; + + unsigned char dataBuf[MTU]; + void (*on_data)(struct TcpClient* client); + void (*on_disconnect)(struct TcpClient* client); + + pthread_t clientThread; +}; + +typedef struct TcpClient TcpClient; + +#endif \ No newline at end of file diff --git a/include/common/tcpd/tcpserver.h b/include/common/tcpd/tcpserver.h new file mode 100644 index 0000000..a847a17 --- /dev/null +++ b/include/common/tcpd/tcpserver.h @@ -0,0 +1,54 @@ +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef struct { + int sockFd; + struct sockaddr_in addr; + int opt; + + // Called before the client thread runs + void (*on_connect)(TcpClient* client); + // Called when data is received + void (*on_data)(TcpClient* client); + // Called before the socket and client thread are killed; Do NOT free client manually + void (*on_disconnect)(TcpClient* client); + + // max clients + size_t clients; + TcpClient** clientsArrPtr; + + pthread_t svrThread; +} TcpServer; + +struct tcpclient_thread_args { + TcpClient* clientPtr; + TcpServer* serverPtr; +}; + +typedef struct tcpclient_thread_args tcpclient_thread_args; + +TcpServer* TcpServer_Create(); +void TcpServer_Destroy(TcpServer* ptr); + +void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr); +void TcpServer_Start(TcpServer* ptr, int maxcons); +void TcpServer_Stop(TcpServer* ptr); +void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len); +void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli); +void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli); + +size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len); + +#endif \ No newline at end of file diff --git a/include/server/task/taskqueue.h b/include/server/task/taskqueue.h new file mode 100644 index 0000000..1f80962 --- /dev/null +++ b/include/server/task/taskqueue.h @@ -0,0 +1,17 @@ +#ifndef TASKQUEUE_H +#define TASKQUEUE_H + +#include +#include + +typedef struct { + DynArr* tasks; +} task_queue_t; + +void TaskQueue_Init(task_queue_t* queue); +void TaskQueue_DestroyQueue(task_queue_t* queue); +void TaskQueue_AddTask(task_queue_t* queue, task_t* task); +void TaskQueue_RemoveTask(task_queue_t* queue, size_t idx); +void TaskQueue_RemoveTaskByPtr(task_queue_t* queue, task_t* task); + +#endif \ No newline at end of file diff --git a/src/client/main.c b/src/client/main.c new file mode 100644 index 0000000..e1988a3 --- /dev/null +++ b/src/client/main.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("Hello, World!\n"); + return 0; +} \ No newline at end of file diff --git a/src/common/dynarr.c b/src/common/dynarr.c new file mode 100644 index 0000000..6df3dc2 --- /dev/null +++ b/src/common/dynarr.c @@ -0,0 +1,164 @@ +#include + +DynArr* DynArr_create(size_t elemSize, size_t capacity) { + DynArr* p = (DynArr*)malloc(sizeof(DynArr)); + if (!p) return NULL; + + p->elemSize = elemSize; + p->capacity = capacity; + p->data = malloc(elemSize * capacity); + if (!p->data) { + free(p); + return NULL; + } + p->size = 0; + + return p; +} + +// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead. +void DynArr_reserve(DynArr* p, size_t n) { + if (n <= p->capacity) { + printf("reserve ignored; attempted to reserve less or equal to current capacity\n"); + return; + } + + if (n > DYNARR_MAX_CAPACITY) { + printf("reserve ignored; attempted to reserve over 32 bits\n"); + return; + } + + void* new_data = realloc(p->data, n * p->elemSize); + if (!new_data) { + printf("reserve failed\n"); + exit(1); + } + p->data = new_data; + p->capacity = n; +} + +// Push data into a new block at the end of the array +void* DynArr_push_back(DynArr* p, void* value) { + if (value == NULL) { + printf("push_back ignored; value is null"); + return NULL; + } + + if (p->size >= p->capacity) { + size_t new_cap = (p->capacity == 0) ? 1 : p->capacity * 2; + + if (new_cap < p->capacity || new_cap > DYNARR_MAX_CAPACITY) { + printf("push_back ignored; capacity overflow\n"); + return NULL; + } + + void* new_data = realloc(p->data, new_cap * p->elemSize); + if (!new_data) { + printf("push failed\n"); + exit(1); + } + p->capacity = new_cap; + p->data = new_data; + } + + void* dst = (void*)((char*)p->data + (p->size * p->elemSize)); + memcpy((char*)dst, value, p->elemSize); + p->size++; + + return dst; +} + +// Remove the last block in the array. +void DynArr_pop_back(DynArr* p) { + if (p->size == 0) { + printf("pop_back ignored; size is 0\n"); + return; + } + + p->size--; // Will automatically overwrite that memory naturally +} + +// Remove first block from array. +void DynArr_pop_front(DynArr* p) { + if (p->size == 0) { + printf("pop_front ignored; size is 0\n"); + return; + } + + memmove( + (char*)p->data, + (char*)p->data + p->elemSize, + (p->size - 1) * p->elemSize + ); + + p->size--; +} + +// Remove index from array. This moves all blocks after the index block. +void DynArr_remove(DynArr* p, size_t index) { + if (index >= p->size) return; + + memmove( + (char*)p->data + (index * p->elemSize), + (char*)p->data + (index + 1) * p->elemSize, + (p->size - index - 1) * p->elemSize + ); + + p->size--; +} + +// Erase the array. This will not free unused blocks. +void DynArr_erase(DynArr* p) { + p->size = 0; +} + +// Prune and free unused blocks. If pruning to zero, ensure to reserve after. +void DynArr_prune(DynArr* p) { + void* new_data = realloc(p->data, (p->size == 0 ? 1 : p->size) * p->elemSize); + if (!new_data) { + printf("pruning failed\n"); + exit(1); + } + + p->data = new_data; + p->capacity = p->size; +} + +// Get a pointer to a block by index +void* DynArr_at(DynArr* p, size_t index) { + if (index >= p->size) return NULL; + return (char*)p->data + (index * p->elemSize); +} + +// Get the index by block pointer +size_t DynArr_at_ptr(DynArr* p, void* ptr) { + if (!p || !ptr) { + printf("invalid pointer\n"); + exit(1); + } + + for (size_t i = 0; i < p->size; i++) { + if ((void*)(((char*)p->data) + (i * p->elemSize)) == ptr) { + return i; + } + } + + // If for some reason the array has 2^64 elements in it, then fuck it, I guess we'll just crash, I don't care. + return -1; +} + +// Get size +size_t DynArr_size(DynArr* p) { + return p->size; +} + +// Get capacity +size_t DynArr_capacity(DynArr* p) { + return p->capacity; +} + +void DynArr_destroy(DynArr* p) { + if (!p) return; + free(p->data); + free(p); +} \ No newline at end of file diff --git a/src/common/numgen.c b/src/common/numgen.c new file mode 100644 index 0000000..a39fd2a --- /dev/null +++ b/src/common/numgen.c @@ -0,0 +1,17 @@ +#include + +unsigned char random_byte(void) { + return (unsigned char)(rand() % 256); +} + +uint32_t random_four_byte(void) { + uint32_t x; + unsigned char bytes[4]; + for (char i = 0; i < 4; i++) { + bytes[i] = random_byte(); + } + + memcpy(&x, bytes, sizeof(x)); + + return x; +} \ No newline at end of file diff --git a/src/common/task/task.c b/src/common/task/task.c new file mode 100644 index 0000000..34d3af2 --- /dev/null +++ b/src/common/task/task.c @@ -0,0 +1,33 @@ +#include +#include + +// Last encountered error after ANY event. +int TASK_ERR_CODE = TASK_ERR_NONE; + +void Task_Create(task_t* tsk) { + if (!tsk) { + TASK_ERR_CODE = TASK_ERR_INVPTR; + return; + } + + tsk->taskId = random_four_byte(); + tsk->assigned_at = 0; // or NULL, so 1/1/1970 + tsk->assigned_to = 0; // ClientID 0 is reserved + tsk->state = TASK_NONE; + memset(tsk->binary, 0, 64); + tsk->args = DYNARR_CREATE(task_arg_t, 1); +} + +void Task_DestroyArgs(task_t* task) { + if (!task) { + TASK_ERR_CODE = TASK_ERR_INVPTR; + return; + } + + if (!task->args) { + TASK_ERR_CODE = TASK_ERR_INVARGS; + return; + } + + DynArr_destroy(task->args); +} \ No newline at end of file diff --git a/src/common/tcpd/tcpserver.c b/src/common/tcpd/tcpserver.c new file mode 100644 index 0000000..8177275 --- /dev/null +++ b/src/common/tcpd/tcpserver.c @@ -0,0 +1,292 @@ +#include + +TcpServer* TcpServer_Create() { + TcpServer* svr = (TcpServer*)malloc(sizeof(TcpServer)); + + if (!svr) { + perror("tcpserver - creation failure"); + exit(1); + } + + svr->sockFd = -1; + svr->svrThread = 0; + svr->on_connect = NULL; + svr->on_data = NULL; + svr->on_disconnect = NULL; + + svr->clients = 0; + svr->clientsArrPtr = NULL; + + return svr; +} + +void TcpServer_Destroy(TcpServer* ptr) { + if (ptr) { + if (ptr->clientsArrPtr) { + for (size_t i = 0; i < ptr->clients; i++) { + if (ptr->clientsArrPtr[i]) { + free(ptr->clientsArrPtr[i]); + } + } + + free(ptr->clientsArrPtr); + } + + close(ptr->sockFd); + free(ptr); + } +} + +void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr) { + if (ptr) { + // Create socket + ptr->sockFd = socket(AF_INET, SOCK_STREAM, 0); + if (ptr->sockFd < 0) { + perror("tcpserver - socket"); + exit(EXIT_FAILURE); + } + + // Allow quick port resue + ptr->opt = 1; + setsockopt(ptr->sockFd, SOL_SOCKET, SO_REUSEADDR, &ptr->opt, sizeof(int)); + + // Fill address structure + memset(&ptr->addr, 0, sizeof(ptr->addr)); + ptr->addr.sin_family = AF_INET; + ptr->addr.sin_port = htons(port); + inet_pton(AF_INET, addr, &ptr->addr.sin_addr); + + // Bind + if (bind(ptr->sockFd, (struct sockaddr*)&ptr->addr, sizeof(ptr->addr)) < 0) { + perror("tcpserver - bind"); + close(ptr->sockFd); + exit(EXIT_FAILURE); + } + } +} + +// Do not call outside of func. +void* TcpServer_clientthreadprocess(void* ptr) { + if (!ptr) { + perror("Client ptr is null!\n"); + return NULL; + } + + tcpclient_thread_args* args = (tcpclient_thread_args*)ptr; + + TcpClient* cli = args->clientPtr; + TcpServer* svr = args->serverPtr; + + if (args) { + free(args); + } + + while (1) { + memset(cli->dataBuf, 0, MTU); // Reset buffer + ssize_t n = recv(cli->clientFd, cli->dataBuf, MTU, 0); + + if (n == 0) { + break; // Client disconnected + } else if (n > 0) { + if (cli->on_data) { + cli->on_data(cli); + } + } + + pthread_testcancel(); // Check for thread death + } + + cli->on_disconnect(cli); + + // Close on exit + close(cli->clientFd); + + // Destroy + TcpClient** arr = svr->clientsArrPtr; + size_t idx = Generic_FindClientInArrayByPtr(arr, cli, svr->clients); + if (idx != SIZE_MAX) { + if (arr[idx]) { + free(arr[idx]); + arr[idx] = NULL; + } + } else { + perror("tcpserver (client thread) - something already freed the client!"); + } + + //free(ptr); + + return NULL; +} + +// Do not call outside of func. +void* TcpServer_threadprocess(void* ptr) { + if (!ptr) { + perror("Client ptr is null!\n"); + return NULL; + } + + TcpServer* svr = (TcpServer*)ptr; + while (1) { + TcpClient tempclient; + socklen_t clientsize = sizeof(tempclient.clientAddr); + int client = accept(svr->sockFd, (struct sockaddr*)&tempclient.clientAddr, &clientsize); + if (client >= 0) { + tempclient.clientFd = client; + tempclient.on_data = svr->on_data; + tempclient.on_disconnect = svr->on_disconnect; + + // I'm lazy, so I'm just copying the data for now (I should probably make this a better way) + TcpClient* heapCli = (TcpClient*)malloc(sizeof(TcpClient)); + if (!heapCli) { + perror("tcpserver - client failed to allocate"); + exit(EXIT_FAILURE); // Wtf just happened??? + } + + heapCli->clientAddr = tempclient.clientAddr; + heapCli->clientFd = tempclient.clientFd; + heapCli->on_data = tempclient.on_data; + heapCli->on_disconnect = tempclient.on_disconnect; + heapCli->clientId = random_four_byte(); + + size_t i; + for (i = 0; i < svr->clients; i++) { + if (svr->clientsArrPtr[i] == NULL) { + // Make use of that space + svr->clientsArrPtr[i] = heapCli; // We have now transfered the ownership :) + break; + } + } + + if (i == svr->clients) { + // Not found + // RST; Thread doesn't exist yet + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + setsockopt(heapCli->clientFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); + close(heapCli->clientFd); + + free(heapCli); + heapCli = NULL; + //svr->clientsArrPtr[i] = NULL; + + continue; + } + + tcpclient_thread_args* arg = (tcpclient_thread_args*)malloc(sizeof(tcpclient_thread_args)); + arg->clientPtr = heapCli; + arg->serverPtr = svr; + + if (svr->on_connect) { + svr->on_connect(heapCli); + } + + pthread_create(&heapCli->clientThread, NULL, TcpServer_clientthreadprocess, arg); + pthread_detach(heapCli->clientThread); // May not work :( + } + + pthread_testcancel(); // Check for thread death + } + + return NULL; +} + +void TcpServer_Start(TcpServer* ptr, int maxcons) { + if (ptr) { + if (listen(ptr->sockFd, maxcons) < 0) { + perror("tcpserver - listen"); + close(ptr->sockFd); + exit(EXIT_FAILURE); + } + + ptr->clients = maxcons; + ptr->clientsArrPtr = (TcpClient**)malloc(sizeof(TcpClient*) * maxcons); + + if (!ptr->clientsArrPtr) { + perror("tcpserver - allocation of client space fatally errored"); + exit(EXIT_FAILURE); + } + + // Fucking null out everything + for (int i = 0; i < maxcons; i++) { + ptr->clientsArrPtr[i] = NULL; + } + } + + // Spawn server thread + pthread_create(&ptr->svrThread, NULL, TcpServer_threadprocess, ptr); +} + +void TcpServer_Stop(TcpServer* ptr) { + if (ptr && ptr->svrThread != 0) { + // Stop server + pthread_cancel(ptr->svrThread); + pthread_join(ptr->svrThread, NULL); + + // Disconnect clients + for (size_t i = 0; i < ptr->clients; i++) { + TcpClient* cliPtr = ptr->clientsArrPtr[i]; + if (cliPtr) { + close(cliPtr->clientFd); + pthread_cancel(cliPtr->clientThread); + } + } + + ptr->svrThread = 0; + } +} + +void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len) { + if (ptr && cli && data && len > 0) { + send(cli->clientFd, data, len, 0); + } +} + +void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli) { + if (ptr && cli) { + close(cli->clientFd); + pthread_cancel(cli->clientThread); + + size_t idx = Generic_FindClientInArrayByPtr(ptr->clientsArrPtr, cli, ptr->clients); + if (idx != SIZE_MAX) { + if (ptr->clientsArrPtr[idx]) { + free(ptr->clientsArrPtr[idx]); + } + ptr->clientsArrPtr[idx] = NULL; + } else { + perror("tcpserver - didn't find client to disconnect in array!"); + } + } +} + +void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli) { + if (ptr && cli) { + // RST the connection + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + setsockopt(cli->clientFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); + close(cli->clientFd); + pthread_cancel(cli->clientThread); + + size_t idx = Generic_FindClientInArrayByPtr(ptr->clientsArrPtr, cli, ptr->clients); + if (idx != SIZE_MAX) { + if (ptr->clientsArrPtr[idx]) { + free(ptr->clientsArrPtr[idx]); + } + ptr->clientsArrPtr[idx] = NULL; + } else { + perror("tcpserver - didn't find client to kill in array!"); + } + } +} + +size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len) { + for (size_t i = 0; i < len; i++) { + if (arr[i] == ptr) { + return i; + } + } + + return SIZE_MAX; // Returns max unsigned, likely improbable to be correct +} \ No newline at end of file diff --git a/src/server/main.c b/src/server/main.c new file mode 100644 index 0000000..a2760d6 --- /dev/null +++ b/src/server/main.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +volatile int running = 1; + +void signalHandler(int sig) { + running = 0; +} + +int main(void) { + signal(SIGINT, signalHandler); + + task_queue_t taskQueue; + TaskQueue_Init(&taskQueue); + + printf("Task Queue Created!\n"); + task_t task1; + Task_Create(&task1); + task1.state = TASK_PENDING; + + TaskQueue_AddTask(&taskQueue, &task1); + printf("Task added!\n"); + + while (running) { + // Main Loop + sleep(1); + } + + TaskQueue_RemoveTask(&taskQueue, 0); + TaskQueue_DestroyQueue(&taskQueue); + + printf("Destroyed!\n"); + + return 0; +} \ No newline at end of file diff --git a/src/server/task/taskqueue.c b/src/server/task/taskqueue.c new file mode 100644 index 0000000..e5ece39 --- /dev/null +++ b/src/server/task/taskqueue.c @@ -0,0 +1,93 @@ +#include + +void TaskQueue_Init(task_queue_t* queue) { + if (!queue) { + TASK_ERR_CODE = TASK_ERR_INVQUEUE; + return; + } + + queue->tasks = DYNARR_CREATE(task_t, 1); +} + +void TaskQueue_DestroyQueue(task_queue_t* queue) { + if (!queue) { + TASK_ERR_CODE = TASK_ERR_INVQUEUE; + return; + } + + if (!queue->tasks) { + TASK_ERR_CODE = TASK_ERR_INVPTR; + return; + } + + DynArr_destroy(queue->tasks); +} + +void TaskQueue_AddTask(task_queue_t* queue, task_t* task) { + if (!queue) { + TASK_ERR_CODE = TASK_ERR_INVQUEUE; + return; + } + + if (!queue->tasks) { + TASK_ERR_CODE = TASK_ERR_INVPTR; + return; + } + + if (!task) { + TASK_ERR_CODE = TASK_ERR_INVTASK; + return; + } + + DynArr_push_back(queue->tasks, task); +} + +void TaskQueue_RemoveTask(task_queue_t* queue, size_t idx) { + if (!queue) { + TASK_ERR_CODE = TASK_ERR_INVQUEUE; + return; + } + + if (!queue->tasks) { + TASK_ERR_CODE = TASK_ERR_INVPTR; + return; + } + + size_t n = DynArr_size(queue->tasks); + + if (idx >= n) { // Index adjusted + TASK_ERR_CODE = TASK_ERR_INVIDX; + return; + } + + task_t* ptr = DynArr_at(queue->tasks, idx); + Task_DestroyArgs(ptr); + + DynArr_remove(queue->tasks, n); +} + +void TaskQueue_RemoveTaskByPtr(task_queue_t* queue, task_t* task) { + if (!queue) { + TASK_ERR_CODE = TASK_ERR_INVQUEUE; + return; + } + + if (!queue->tasks) { + TASK_ERR_CODE = TASK_ERR_INVPTR; + return; + } + + if (!task) { + TASK_ERR_CODE = TASK_ERR_INVTASK; + return; + } + + size_t at = DynArr_at_ptr(queue->tasks, task); + + if (at == SIZE_MAX) { + TASK_ERR_CODE = TASK_ERR_INVIDX; + return; + } + + DynArr_remove(queue->tasks, at); +} \ No newline at end of file