diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fdfc1e6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.17) +project(flint C) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_C_STANDARD 99) +include_directories(include) + +set(SOURCES + src/linkedlist.c + src/set.c + src/stack.c + src/binarytree.c + src/input.c + src/math.c + src/string.c + src/vector.c + src/utility.c + src/crypto.c + src/parsing.c + src/network.c +) + +if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")) + add_library(flint ${SOURCES} src/macos/macos.c) +else() + add_library(flint ${SOURCES}) +endif() + +if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) + target_link_libraries(flint pthread bsd) +endif() + +if(${CMAKE_PROJECT_NAME} STREQUAL flint) + add_executable(tests tests/tests.c) + target_include_directories(tests PRIVATE include) + + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_link_libraries(tests flint pthread bsd) + else() + target_link_libraries(tests flint pthread) + endif() + + add_executable(netmanual tests/netmanual.c) + target_include_directories(netmanual PRIVATE include) + + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_link_libraries(netmanual flint pthread bsd) + else() + target_link_libraries(netmanual flint pthread) + endif() +endif() diff --git a/docs/input.md b/docs/input.md index 8537d0e..dae3f84 100644 --- a/docs/input.md +++ b/docs/input.md @@ -92,7 +92,8 @@ printf("%s\n", sp[0]); // Prints "Split" ### del_split -Frees all memory used by `split()`. Just like `split`, it does not touch the original string +Frees all memory used by `split()`. Just like `split`, it does not touch the original string. + ```c void del_split(char **sp); @@ -101,3 +102,19 @@ size_t sp_sz = 0; char **sp = split("Delete Me!", &sp_sz, " "); void del_split(sp); ``` + +### capture_system + +Runs a command on the system shell and returns stdout as a string. `buffsize` is the size of +the returned buffer that holds `stdout`. Passing `0` to `buffsize` will use the default buffer size of `1024`. + +User is responsible for freeing the returned string. + +```c +const char *capture_system(const char *cmd, int buffsize); + +/* Usage */ +const char *cap = capture_system("ls $HOME", 0); +printf("%s\n", cap); +free(cap); +``` \ No newline at end of file diff --git a/include/lfinput.h b/include/lfinput.h index 53ecb0c..3e5bc23 100644 --- a/include/lfinput.h +++ b/include/lfinput.h @@ -17,4 +17,8 @@ void del_split(char **); void del_lines(char **); +#define DEFAULT_CAPTURE_SYSTEM_BUFSIZE 1024 + +const char *capture_system(const char *cmd, int buf_sz); + #endif // LIBFLINT_INPUT_H diff --git a/src/input.c b/src/input.c index f20b820..23d54c9 100644 --- a/src/input.c +++ b/src/input.c @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef __linux__ @@ -121,3 +122,18 @@ void del_split(char **sp) { void del_lines(char **lines) { del_split(lines); } + +const char *capture_system(const char *cmd, int buf_sz) { + if (buf_sz == 0) { + buf_sz = DEFAULT_CAPTURE_SYSTEM_BUFSIZE; + } + char *buf = malloc(buf_sz); + FILE *tmp = popen(cmd, "r"); + if (tmp == NULL) { + fprintf(stderr, "libflint: failed to open FILE *tmp in capture_system. Errno: %d\n", errno); + free(buf); + return NULL; + } + fgets(buf, buf_sz, tmp); + return buf; +} \ No newline at end of file diff --git a/src/network.c b/src/network.c index 637ed17..711f24b 100644 --- a/src/network.c +++ b/src/network.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "lfnetwork.h" @@ -95,50 +96,47 @@ int serve(Server *s, int backlog_size) { return 1; } - printf("Server is waiting for connections on port %d...\n", s->port); s->handler(s); return 0; } -void handler_tcp_echo(Server *s) { - struct sockaddr_storage client_addr; +static void *tcp_echo_thread(void *vargp) { while (1) { - socklen_t client_addr_sz = sizeof(client_addr); - int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz); - if (new_fd == -1) { - printf("failed to accept. Errno: %d\n", errno); - continue; - } + char recv_buf[256]; + int fd = *(int *) vargp; - char buf[33]; - inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); - printf("Received connection from %s\n", buf); - - // Start child process - if (!fork()) { - close(s->fd); // Child doesn't need the initial socket - - char recv_buf[256]; - int r = recv(new_fd, recv_buf, 256, 0); + int r = (int)recv(fd, recv_buf, 256, 0); + if (r < 1) { if (r == -1) { fprintf(stderr, "Failed to recv. Errno: %d\n", errno); - goto CHILD_END; - } else if (r == 0) { - fprintf(stderr, "Client closed connection\n"); - goto CHILD_END; } + close(fd); + break; + } - if (send(new_fd, recv_buf, strlen(recv_buf), 0) == -1) { - fprintf(stderr, "Failed to send echo\n"); - } - -CHILD_END: - close(new_fd); - _exit(0); - } - // End child process - - close(new_fd); // new_fd is not used by the parent + if (send(fd, recv_buf, strlen(recv_buf), 0) == -1) { + fprintf(stderr, "Failed to send echo. Errno: %d\n", errno); + close(fd); + break; + } + } +} + +void handler_tcp_echo(Server *s) { + while (1) { + struct sockaddr_storage client_addr; + socklen_t client_addr_sz = sizeof(client_addr); + int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz); + if (new_fd == -1) { + fprintf(stderr, "failed to accept. Errno: %d\n", errno); + continue; + } + + pthread_t srv_tid; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&srv_tid, &attr, tcp_echo_thread, &new_fd); } } diff --git a/tests/netmanual.c b/tests/netmanual.c index 60ede69..c1eb3df 100644 --- a/tests/netmanual.c +++ b/tests/netmanual.c @@ -1,11 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include - #include "lfnetwork.h" int main(int argc, char **argv) { diff --git a/tests/tcptest.c b/tests/tcptest.c deleted file mode 100644 index 4d3c7ee..0000000 --- a/tests/tcptest.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "lfnetwork.h" - -void test_handler(Server *s) { - struct sockaddr_storage client_addr; - socklen_t client_addr_sz = sizeof(client_addr); - int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz); - if (new_fd == -1) { - printf("failed to accept. Errno: %d\n", errno); - return; - } - - char buf[33]; - inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); - printf("Received connection from %s\n", buf); - - if (send(new_fd, "TEST SEND", 10, 0) == -1) { - printf("Failed to send hello world. Errno: %d\n", errno); - } - close(new_fd); -} - -int main(int argc, char **argv) { - Server *server = new_server(SERVERTYPE_TCP, "18632", test_handler); - serve(server, DEFAULT_BACKLOG); - delete_server(server); -} diff --git a/tests/tests.c b/tests/tests.c index 334263f..1537423 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include "lflinkedlist.h" #include "lfnetwork.h" @@ -16,6 +18,7 @@ #if defined(__APPLE__) || defined(__MACH__) #include "lfmacos.h" +#include "lfinput.h" #endif /* defined(__APPLE__) || defined(__MACH__) */ void print_ll(List *list) { @@ -396,6 +399,61 @@ void test_parsing() { printf("Passes all parsing tests\n"); } +#define NET_MSG "TEST SEND" + +void tcp_test_handler(Server *s) { + struct sockaddr_storage client_addr; + socklen_t client_addr_sz = sizeof(client_addr); + int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz); + assert(new_fd != -1); + assert(send(new_fd, NET_MSG, 10, 0) != -1); + close(new_fd); +} + +void *tcp_server_thread(void *vargp) { + Server *server = new_server(SERVERTYPE_TCP, "18632", tcp_test_handler); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +} + +void udp_test_handler(Server *s) { + struct sockaddr_storage client_addr; + socklen_t client_addr_sz = sizeof(client_addr); + char recv_buf[128]; + + int r = (int)recvfrom(s->fd, recv_buf, 128, 0, (struct sockaddr*)&client_addr, &client_addr_sz); + assert(r > 0); + assert(strcmp(recv_buf, NET_MSG) == 0); +} + +void *udp_server_thread(void *vargp) { + Server *server = new_server(SERVERTYPE_UDP, "18633", udp_test_handler); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +} + +void test_network() { + printf("\n--- NETWORK TEST ---\n"); + pthread_t srv_tid; + pthread_create(&srv_tid, NULL, tcp_server_thread, NULL); + + sleep(1); + const char *s = capture_system("echo hello | nc localhost 18632", 0); + assert(strcmp(s, NET_MSG) == 0); + free((char *)s); + + pthread_join(srv_tid, NULL); + printf("Passed TCP test\n"); + + pthread_create(&srv_tid, NULL, udp_server_thread, NULL); + sleep(1); + system("echo hello | nc localhost 18633"); + assert(strcmp(s, NET_MSG) == 0); + + pthread_join(srv_tid, NULL); + printf("Passed UDP test\n"); +} + #if defined(__APPLE__) || defined(__MACH__) void test_macos() { printf("\n--- macOS TEST ---\n"); @@ -421,6 +479,7 @@ int main() { test_string(); test_crypto(); test_parsing(); + test_network(); #if defined(__APPLE__) || defined(__MACH__) test_macos();