commit 133d9609195310f1fd43622942b9ced22752e68f Author: Serafim Date: Thu Dec 12 04:09:30 2024 +0300 init diff --git a/client/client b/client/client new file mode 100755 index 0000000..1fb5a66 Binary files /dev/null and b/client/client differ diff --git a/client/client.cpp b/client/client.cpp new file mode 100644 index 0000000..ee554e5 --- /dev/null +++ b/client/client.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// IP address of the server1 +std::string SERVER_IP1 = "127.0.0.1"; + +// Port of the server1 +int PORT1 = 9001; + +// IP address of the server2 +std::string SERVER_IP2 = "127.0.0.1"; + +// Port of the server2 +int PORT2 = 9002; + +int client_socket1 = -1; +int client_socket2 = -1; + +void* recieve(void* arg) { + int client_socket = *(int*)arg; + delete (int*)arg; + + while (true) { + char buffer[128]; + ssize_t recieved = recv(client_socket, buffer, sizeof(buffer), 0); + if (recieved == 0 || recieved == -1) { + break; + } + std::cout << buffer << std::endl; + } + + return nullptr; +} + +void* timer(void* arg) { + int client_socket = *(int*)arg; + delete (int*)arg; + + while (true) { + send(client_socket, "", 1, 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + return nullptr; +} + +int main() { + pthread_t t1 = -1, t2 = -1; + + std::cout << "Usage:\n\tconnectN - connect to the server\n\tdisconnectN - disconnect from the server\n\tsendN - send request to the server\n\ttimerN - begin polling of the server\n\tstopN - stop polling of the server\n\tquit - quit" << std::endl; + + while(true) { + std::string command; + + std::cin >> command; + + if(command == "quit") { + break; + } else if (command == "connect1") { + client_socket1 = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in server_address1; + server_address1.sin_family = AF_INET; + server_address1.sin_port = htons(PORT1); + server_address1.sin_addr.s_addr = inet_addr(SERVER_IP1.c_str()); + if (connect(client_socket1, (struct sockaddr*)&server_address1, sizeof(server_address1)) == 0) { + std::cout << "Connected to the server 1" << std::endl; + pthread_t tid1; + int* client_socket_copy1 = new int(client_socket1); + pthread_create(&tid1, nullptr, &recieve, client_socket_copy1); + } else { + std::cout << "Failed to connect " << std::strerror(errno) << std::endl; + } + } else if (command == "connect2") { + client_socket2 = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in server_address2; + server_address2.sin_family = AF_INET; + server_address2.sin_port = htons(PORT2); + server_address2.sin_addr.s_addr = inet_addr(SERVER_IP2.c_str()); + if (connect(client_socket2, (struct sockaddr*)&server_address2, sizeof(server_address2)) == 0) { + std::cout << "Connected to the server 2" << std::endl; + pthread_t tid2; + int* client_socket_copy2 = new int(client_socket2); + pthread_create(&tid2, nullptr, &recieve, client_socket_copy2); + } else { + std::cout << "Failed to connect " << std::strerror(errno) << std::endl; + } + } else if (command == "disconnect1") { + if (client_socket1 != -1) { + close(client_socket1); + client_socket1 = -1; + std::cout << "Disconnected" << std::endl; + } else { + std::cout << "Server is not connected" << std::endl; + } + } else if (command == "disconnect2") { + if (client_socket2 != -1) { + close(client_socket1); + client_socket2 = -1; + std::cout << "Disconnected" << std::endl; + } else { + std::cout << "Server is not connected" << std::endl; + } + } else if (command == "send1") { + if (send(client_socket1, "", 1, 0) == -1) { + std::cout << "Server is not connected" << std::endl; + } else { + std::cout << "Message sent" << std::endl; + } + } else if (command == "send2") { + if (send(client_socket2, "", 1, 0) == -1) { + std::cout << "Server is not connected" << std::endl; + } else { + std::cout << "Message sent" << std::endl; + } + } else if (command == "timer1") { + if (client_socket1 != -1) { + int* client_socket_copy1 = new int(client_socket1); + pthread_create(&t1, nullptr, &timer, client_socket_copy1); + pthread_detach(t1); + std::cout << "Timer created" << std::endl; + } else { + std::cout << "Server is not connected" << std::endl; + } + } else if (command == "timer2") { + if (client_socket1 != -1) { + int* client_socket_copy2 = new int(client_socket2); + pthread_create(&t2, nullptr, &timer, client_socket_copy2); + pthread_detach(t2); + std::cout << "Timer created" << std::endl; + } else { + std::cout << "Server is not connected" << std::endl; + } + } else if (command == "stop1") { + if (t1 != -1) { + pthread_cancel(t1); + t1 = -1; + std::cout << "Timer removed" << std::endl; + } else { + std::cout << "Timer is not running" << std::endl; + } + } else if (command == "stop2") { + if (t2 != -1) { + pthread_cancel(t2); + t2 = -1; + std::cout << "Timer removed" << std::endl; + } else { + std::cout << "Timer is not running" << std::endl; + } + } else { + std::cout << "Unknown command" << std::endl; + } + } + + if (t1 != -1) { + pthread_cancel(t1); + } + + if (t2 != -1) { + pthread_cancel(t2); + } + + close(client_socket1); + close(client_socket2); + + return 0; +} \ No newline at end of file diff --git a/client/run.sh b/client/run.sh new file mode 100755 index 0000000..b63712d --- /dev/null +++ b/client/run.sh @@ -0,0 +1,2 @@ +g++ -o client client.cpp +./client \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..dcb4c75 --- /dev/null +++ b/compose.yml @@ -0,0 +1,7 @@ +services: + log: + build: + context: ./log + volumes: + - &log_data ./log_data:/log_data + - &tmp ./tmp:/pipe diff --git a/libs.txt b/libs.txt new file mode 100644 index 0000000..225e3a8 --- /dev/null +++ b/libs.txt @@ -0,0 +1,2 @@ +libncurses5-dev +libncursesw5-dev diff --git a/log/Dockerfile b/log/Dockerfile new file mode 100644 index 0000000..2a7dee2 --- /dev/null +++ b/log/Dockerfile @@ -0,0 +1,5 @@ +FROM frolvlad/alpine-gxx +COPY . /app +WORKDIR /app +RUN g++ -o log log.cpp +CMD ["./log"] diff --git a/log/log b/log/log new file mode 100755 index 0000000..07aac05 Binary files /dev/null and b/log/log differ diff --git a/log/log.cpp b/log/log.cpp new file mode 100644 index 0000000..4144e56 --- /dev/null +++ b/log/log.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +std::string LOG_DIR = "/log_data"; + +void log(std::string msg) { + mkdir(LOG_DIR.c_str(), 0755); + time_t now = time(0); + struct tm tstruct; + tstruct = *localtime(&now); + char filename[12]; + std::strftime(filename, 12, "%Y-%m-%d", &tstruct); + std::ofstream log_file; + log_file.open(LOG_DIR + "/" + filename + ".log", std::ios_base::app); + char timestamp[32]; + std::strftime(timestamp, 32, "%Y/%m/%d %H:%M:%S ", &tstruct); + log_file << timestamp << msg << std::endl; + log_file.close(); +} + +int main() { + log("uwu"); + log("owo"); + log("yay"); + + return 0; +} \ No newline at end of file diff --git a/log/run.sh b/log/run.sh new file mode 100755 index 0000000..84055e5 --- /dev/null +++ b/log/run.sh @@ -0,0 +1,2 @@ +g++ -o log log.cpp +./log \ No newline at end of file diff --git a/log_data/2024-12-12.log b/log_data/2024-12-12.log new file mode 100644 index 0000000..c34ba05 --- /dev/null +++ b/log_data/2024-12-12.log @@ -0,0 +1,6 @@ +2024/12/12 00:24:34 uwu +2024/12/12 00:24:34 owo +2024/12/12 00:24:34 yay +2024/12/12 00:24:50 uwu +2024/12/12 00:24:50 owo +2024/12/12 00:24:50 yay diff --git a/server1/run.sh b/server1/run.sh new file mode 100755 index 0000000..2ce05b3 --- /dev/null +++ b/server1/run.sh @@ -0,0 +1,2 @@ +g++ -o server server.cpp -lncurses +./server \ No newline at end of file diff --git a/server1/server b/server1/server new file mode 100755 index 0000000..aa7bfcd Binary files /dev/null and b/server1/server differ diff --git a/server1/server.cpp b/server1/server.cpp new file mode 100644 index 0000000..55d3eac --- /dev/null +++ b/server1/server.cpp @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Configuration */ + +// Name of the pipe used to transfer logs to the log server +std::string LOG_FIFO_NAME = "./tmp/log"; + +// Type of the server: server1 - last error & cursor position, server2 - mem % & vmem % +std::string SERVER_TYPE = "server1"; + +// Port to run server +int PORT = 9001; + +// Maximum amount of socket connections +int MAX_CONNECTIONS = 128; + +// Amount of log messages displayed on screen +const int CMD_LOG_BUFFER = 10; + +// Amount of milliseconds between screen rerenders +int CMD_REFRESH_TIME = 500; + + +/* Current status to display */ + +// Buffer with recent log messages +std::string last_log_messages[CMD_LOG_BUFFER]; + +// Amount of active client sessions served by different threads +int active_connections = 0; + +// Total amount of handled requests +int handndled_requests = 0; + +// Amount of active timers for sending messages +int active_timer_threads = 0; + + +// Initializes ncurses lib +void init_ncurses() { + initscr(); + cbreak(); + noecho(); + clear(); +} + +// Returns info about last error +std::string get_last_error() { + const int error_code = errno; + const char* error_description = std::strerror(errno); + return "Last error: " + std::to_string(error_code) + " - " + error_description + "\n"; +} + +// void disable_buffering() { +// struct termios t; +// tcgetattr(STDIN_FILENO, &t); // Get the terminal settings +// t.c_lflag &= ~ICANON; // Disable canonical mode (no buffering) +// t.c_lflag &= ~ECHO; // Disable echo (no terminal output) +// t.c_cc[VMIN] = 1; // Set the minimum number of characters to read (1 char) +// t.c_cc[VTIME] = 0; // Disable timeout +// tcsetattr(STDIN_FILENO, TCSANOW, &t); // Apply the new settings +// } + +// void restore_buffering() { +// struct termios t; +// tcgetattr(STDIN_FILENO, &t); // Get the terminal settings +// t.c_lflag |= ICANON; // Enable canonical mode (buffering) +// t.c_lflag |= ECHO; // Enable echo +// tcsetattr(STDIN_FILENO, TCSANOW, &t); // Apply the restored settings +// } + +// Returns info about current cursor position in console +std::string get_cursor_position() { + int x, y; + + // // Disable buffering + // disable_buffering(); + + // printf("\033[6n"); // Request cursor position + // scanf("\033[%d;%dR", &x, &y); // Read cursor position + + // // Print the cursor position + // printf("Cursor position: (%d, %d)\n", x, y); + + // // Restore the terminal settings + // restore_buffering(); + + getyx(stdscr, y, x); + + return "Cursor position: y: " + std::to_string(y) + " x: " + std::to_string(x) + "\n"; +} + +// Returns info about used physical memory +std::string get_mem_percent() { + std::ifstream meminfo("/proc/meminfo"); + if(!meminfo) { + return "Error: failed to open /proc/meminfo\n"; + } + + std::string line; + long free_mem = -1, total_mem = -1; + + while((std::getline(meminfo, line))) { + if(line.find("MemAvailable:") == 0) { + std::sscanf(line.c_str(), "MemAvailable: %ld kB", &free_mem); + } else if(line.find("MemTotal:") == 0) { + std::sscanf(line.c_str(), "MemTotal: %ld kB", &total_mem); + } + + if(free_mem != -1 && total_mem != -1) { + break; + } + } + + meminfo.close(); + + if(total_mem == -1 || free_mem == -1) { + return "Error: failed to read info from /proc/meminfo\n"; + } + + return "Physical memory: " + std::to_string(100 * (total_mem - free_mem)/total_mem) + "%\n"; +} + +// Returns message to send according to the server type +std::string get_sysinfo() { + if (SERVER_TYPE == "server1") { + return '\t' + get_last_error() + "\t\t\t" + get_cursor_position(); + } + return '\t' + get_mem_percent() + "\t\t\t" + "WIP\n"; // TODO: vmem +} + +// Writes log message on screen and passes it to the logging pipe +void log(std::string msg) { + // Add log message to the screen buffer + for (int i = CMD_LOG_BUFFER - 1; i > 0; i--) { + last_log_messages[i] = last_log_messages[i-1]; + } + time_t now = time(0); + struct tm tstruct; + tstruct = *localtime(&now); + char timestamp[32]; + std::strftime(timestamp, 32, "%H:%M:%S ", &tstruct); + last_log_messages[0] = timestamp + std::string(" ") + msg; + + // Write message to the logging pipe TODO + // int fd = open(LOG_FIFO_NAME.c_str(), O_WRONLY); + // if(fd != -1) { + // write(fd, msg.c_str(), strlen(msg.c_str()) + 1); + // close(fd); + // } +} + +// Opens file descriptor for logging pipe TODO +void init_log_pipe() { + // mkfifo(LOG_FIFO_NAME.c_str(), 0666); +} + +// Returns file descriptor for server socket +int start_socket() { + log("Creating socket..."); + + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(PORT); + server_address.sin_addr.s_addr = INADDR_ANY; + + bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)); + + if (errno == 98) { + log("Error: " + std::string(std::strerror(errno))); + return -1; + } + + log("Socket created"); + + int status = listen(server_socket, MAX_CONNECTIONS); + + if (status != 0) { + log("Error: can't listen with socket"); + return -1; + } + + log("Listening for connections..."); + + return server_socket; +} + +// Handles one connection in sepparate thread +void* handle_connection(void* arg) { + // Cast client socket descriptor + int client_socket = *(int*)arg; + delete (int*)arg; + + std::string info = ""; + + log("New thread created for socket " + std::to_string(client_socket)); + + while (true) { + // Recieve message from client + char buffer[5]; + ssize_t bytes_recieved = recv(client_socket, buffer, sizeof(buffer), 0); + + // If connection closed + if (bytes_recieved == 0) { + log("Connection with client " + std::to_string(client_socket) + " closed"); + active_connections--; + break; + } + + if (bytes_recieved == -1) { + log("Error while recieving message: " + std::to_string(errno) + " - " + std::strerror(errno)); + break; + } + + log("Got new message from client " + std::to_string(client_socket) + ": " + buffer); + + std::string new_info = get_sysinfo(); + + if (info != new_info) { + log("Client " + std::to_string(client_socket) + " info is not up to date"); + info = new_info; + time_t now = time(0); + struct tm tstruct; + tstruct = *localtime(&now); + char timestamp[32]; + std::strftime(timestamp, 32, "%Y/%m/%d %H:%M:%S ", &tstruct); + std::string msg = timestamp + info; + send(client_socket, msg.c_str(), strlen(msg.c_str()) + 1, 0); + log("Sent new info to client " + std::to_string(client_socket)); + } else { + log("Actual info already on client " + std::to_string(client_socket)); + } + } + + close(client_socket); + + return nullptr; +} + +// Refreshes console output +void* draw(void* arg) { + while (true) { + // Update info + clear(); + + // Print configuration + printw( + "LOG_FIFO_NAME=%s\nSERVER_TYPE=%s\nPORT=%i\nMAX_CONNECTIONS=%i\nCMD_LOG_BUFFER=%i\nCMD_REFRESH_TIME=%i\n\n", + LOG_FIFO_NAME.c_str(), SERVER_TYPE.c_str(), PORT, MAX_CONNECTIONS, CMD_LOG_BUFFER, CMD_REFRESH_TIME + ); + + // Print statistics + printw("Active connections: %i\nHandled messages: %i\nActive timers: %i\n", active_connections, handndled_requests, active_timer_threads); + + // Print logs + for (int i = CMD_LOG_BUFFER-1; i >= 0; i--) { + printw("\n%s", last_log_messages[i].c_str()); + } + + refresh(); + + // Wait to update again + std::this_thread::sleep_for(std::chrono::milliseconds(CMD_REFRESH_TIME)); + } +} + +int main() { + init_ncurses(); + init_log_pipe(); + + // Create thread to redraw console output + pthread_t tid; + if (pthread_create(&tid, nullptr, &draw, nullptr) != 0) { + std::cout << ("Failed to create drawing thread") << std::endl; + // endwin(); + return 2; + } + pthread_detach(tid); + + int server_socket = start_socket(); + + while (true) { + int client_socket = accept(server_socket, nullptr, nullptr); + + if (client_socket == -1) { + continue; + } + + log("New connection accepted"); + active_connections++; + + // Create thread for each connection + int* client_socket_copy = new int(client_socket); + pthread_t tid; + if (pthread_create(&tid, nullptr, &handle_connection, client_socket_copy) != 0) { + log("Failed to create thread for the connection"); + delete client_socket_copy; + } + pthread_detach(tid); + } + + return 0; +} diff --git a/server2/run.sh b/server2/run.sh new file mode 100755 index 0000000..2ce05b3 --- /dev/null +++ b/server2/run.sh @@ -0,0 +1,2 @@ +g++ -o server server.cpp -lncurses +./server \ No newline at end of file diff --git a/server2/server b/server2/server new file mode 100755 index 0000000..93968c3 Binary files /dev/null and b/server2/server differ diff --git a/server2/server.cpp b/server2/server.cpp new file mode 100644 index 0000000..d6e14d8 --- /dev/null +++ b/server2/server.cpp @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Configuration */ + +// Name of the pipe used to transfer logs to the log server +std::string LOG_FIFO_NAME = "./tmp/log"; + +// Type of the server: server1 - last error & cursor position, server2 - mem % & vmem % +std::string SERVER_TYPE = "server2"; + +// Port to run server +int PORT = 9002; + +// Maximum amount of socket connections +int MAX_CONNECTIONS = 128; + +// Amount of log messages displayed on screen +const int CMD_LOG_BUFFER = 10; + +// Amount of milliseconds between screen rerenders +int CMD_REFRESH_TIME = 500; + + +/* Current status to display */ + +// Buffer with recent log messages +std::string last_log_messages[CMD_LOG_BUFFER]; + +// Amount of active client sessions served by different threads +int active_connections = 0; + +// Total amount of handled requests +int handndled_requests = 0; + +// Amount of active timers for sending messages +int active_timer_threads = 0; + + +// Initializes ncurses lib +void init_ncurses() { + initscr(); + cbreak(); + noecho(); + clear(); +} + +// Returns info about last error +std::string get_last_error() { + const int error_code = errno; + const char* error_description = std::strerror(errno); + return "Last error: " + std::to_string(error_code) + " - " + error_description + "\n"; +} + +// void disable_buffering() { +// struct termios t; +// tcgetattr(STDIN_FILENO, &t); // Get the terminal settings +// t.c_lflag &= ~ICANON; // Disable canonical mode (no buffering) +// t.c_lflag &= ~ECHO; // Disable echo (no terminal output) +// t.c_cc[VMIN] = 1; // Set the minimum number of characters to read (1 char) +// t.c_cc[VTIME] = 0; // Disable timeout +// tcsetattr(STDIN_FILENO, TCSANOW, &t); // Apply the new settings +// } + +// void restore_buffering() { +// struct termios t; +// tcgetattr(STDIN_FILENO, &t); // Get the terminal settings +// t.c_lflag |= ICANON; // Enable canonical mode (buffering) +// t.c_lflag |= ECHO; // Enable echo +// tcsetattr(STDIN_FILENO, TCSANOW, &t); // Apply the restored settings +// } + +// Returns info about current cursor position in console +std::string get_cursor_position() { + int x, y; + + // // Disable buffering + // disable_buffering(); + + // printf("\033[6n"); // Request cursor position + // scanf("\033[%d;%dR", &x, &y); // Read cursor position + + // // Print the cursor position + // printf("Cursor position: (%d, %d)\n", x, y); + + // // Restore the terminal settings + // restore_buffering(); + + getyx(stdscr, y, x); + + return "Cursor position: y: " + std::to_string(y) + " x: " + std::to_string(x) + "\n"; +} + +// Returns info about used physical memory +std::string get_mem_percent() { + std::ifstream meminfo("/proc/meminfo"); + if(!meminfo) { + return "Error: failed to open /proc/meminfo\n"; + } + + std::string line; + long free_mem = -1, total_mem = -1; + + while((std::getline(meminfo, line))) { + if(line.find("MemAvailable:") == 0) { + std::sscanf(line.c_str(), "MemAvailable: %ld kB", &free_mem); + } else if(line.find("MemTotal:") == 0) { + std::sscanf(line.c_str(), "MemTotal: %ld kB", &total_mem); + } + + if(free_mem != -1 && total_mem != -1) { + break; + } + } + + meminfo.close(); + + if(total_mem == -1 || free_mem == -1) { + return "Error: failed to read info from /proc/meminfo\n"; + } + + return "Physical memory: " + std::to_string(100 * (total_mem - free_mem)/total_mem) + "%\n"; +} + +// Returns message to send according to the server type +std::string get_sysinfo() { + if (SERVER_TYPE == "server1") { + return '\t' + get_last_error() + "\t\t\t" + get_cursor_position(); + } + return '\t' + get_mem_percent() + "\t\t\t" + "WIP\n"; // TODO: vmem +} + +// Writes log message on screen and passes it to the logging pipe +void log(std::string msg) { + // Add log message to the screen buffer + for (int i = CMD_LOG_BUFFER - 1; i > 0; i--) { + last_log_messages[i] = last_log_messages[i-1]; + } + time_t now = time(0); + struct tm tstruct; + tstruct = *localtime(&now); + char timestamp[32]; + std::strftime(timestamp, 32, "%H:%M:%S ", &tstruct); + last_log_messages[0] = timestamp + std::string(" ") + msg; + + // Write message to the logging pipe TODO + // int fd = open(LOG_FIFO_NAME.c_str(), O_WRONLY); + // if(fd != -1) { + // write(fd, msg.c_str(), strlen(msg.c_str()) + 1); + // close(fd); + // } +} + +// Opens file descriptor for logging pipe TODO +void init_log_pipe() { + // mkfifo(LOG_FIFO_NAME.c_str(), 0666); +} + +// Returns file descriptor for server socket +int start_socket() { + log("Creating socket..."); + + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(PORT); + server_address.sin_addr.s_addr = INADDR_ANY; + + bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)); + + if (errno == 98) { + log("Error: " + std::string(std::strerror(errno))); + return -1; + } + + log("Socket created"); + + int status = listen(server_socket, MAX_CONNECTIONS); + + if (status != 0) { + log("Error: can't listen with socket"); + return -1; + } + + log("Listening for connections..."); + + return server_socket; +} + +// Handles one connection in sepparate thread +void* handle_connection(void* arg) { + // Cast client socket descriptor + int client_socket = *(int*)arg; + delete (int*)arg; + + std::string info = ""; + + log("New thread created for socket " + std::to_string(client_socket)); + + while (true) { + // Recieve message from client + char buffer[5]; + ssize_t bytes_recieved = recv(client_socket, buffer, sizeof(buffer), 0); + + // If connection closed + if (bytes_recieved == 0) { + log("Connection with client " + std::to_string(client_socket) + " closed"); + active_connections--; + break; + } + + if (bytes_recieved == -1) { + log("Error while recieving message: " + std::to_string(errno) + " - " + std::strerror(errno)); + break; + } + + log("Got new message from client " + std::to_string(client_socket) + ": " + buffer); + + std::string new_info = get_sysinfo(); + + if (info != new_info) { + log("Client " + std::to_string(client_socket) + " info is not up to date"); + info = new_info; + time_t now = time(0); + struct tm tstruct; + tstruct = *localtime(&now); + char timestamp[32]; + std::strftime(timestamp, 32, "%Y/%m/%d %H:%M:%S ", &tstruct); + std::string msg = timestamp + info; + send(client_socket, msg.c_str(), strlen(msg.c_str()) + 1, 0); + log("Sent new info to client " + std::to_string(client_socket)); + } else { + log("Actual info already on client " + std::to_string(client_socket)); + } + } + + close(client_socket); + + return nullptr; +} + +// Refreshes console output +void* draw(void* arg) { + while (true) { + // Update info + clear(); + + // Print configuration + printw( + "LOG_FIFO_NAME=%s\nSERVER_TYPE=%s\nPORT=%i\nMAX_CONNECTIONS=%i\nCMD_LOG_BUFFER=%i\nCMD_REFRESH_TIME=%i\n\n", + LOG_FIFO_NAME.c_str(), SERVER_TYPE.c_str(), PORT, MAX_CONNECTIONS, CMD_LOG_BUFFER, CMD_REFRESH_TIME + ); + + // Print statistics + printw("Active connections: %i\nHandled messages: %i\nActive timers: %i\n", active_connections, handndled_requests, active_timer_threads); + + // Print logs + for (int i = CMD_LOG_BUFFER-1; i >= 0; i--) { + printw("\n%s", last_log_messages[i].c_str()); + } + + refresh(); + + // Wait to update again + std::this_thread::sleep_for(std::chrono::milliseconds(CMD_REFRESH_TIME)); + } +} + +int main() { + init_ncurses(); + init_log_pipe(); + + // Create thread to redraw console output + pthread_t tid; + if (pthread_create(&tid, nullptr, &draw, nullptr) != 0) { + std::cout << ("Failed to create drawing thread") << std::endl; + // endwin(); + return 2; + } + pthread_detach(tid); + + int server_socket = start_socket(); + + while (true) { + int client_socket = accept(server_socket, nullptr, nullptr); + + if (client_socket == -1) { + continue; + } + + log("New connection accepted"); + active_connections++; + + // Create thread for each connection + int* client_socket_copy = new int(client_socket); + pthread_t tid; + if (pthread_create(&tid, nullptr, &handle_connection, client_socket_copy) != 0) { + log("Failed to create thread for the connection"); + delete client_socket_copy; + } + pthread_detach(tid); + } + + return 0; +}