#include #include #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; // Mutex to change counter pthread_mutex_t conn_mutex; // Log pipe file descriptor int is_log_ok = false; // 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"; } // Returns info about current cursor position in console std::string get_cursor_position() { int x, y; 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"; } std::string get_vmem() { std::ifstream meminfo("/proc/self/status"); if(!meminfo) { return "Error: failed to open /proc/meminfo\n"; } std::string line; long not_free_mem = -1, total_mem = -1; while((std::getline(meminfo, line))) { if(line.find("VmRSS:") == 0) { std::sscanf(line.c_str(), "VmRSS: %ld kB", ¬_free_mem); } else if(line.find("VmSize:") == 0) { std::sscanf(line.c_str(), "VmSize: %ld kB", &total_mem); } if(not_free_mem != -1 && total_mem != -1) { break; } } meminfo.close(); if(total_mem == -1 || not_free_mem == -1) { return "Error: failed to read info from /proc/meminfo\n"; } return "Virtual memory of the proccess: " + std::to_string(100.0 * not_free_mem/total_mem) + "%\n"; } // 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 int log_fd = open(LOG_FIFO_NAME.c_str(), O_WRONLY); if(log_fd != -1) { is_log_ok = true; write(log_fd, msg.c_str(), msg.length()); close(log_fd); } else { is_log_ok = false; } } // // Opens file descriptor for logging pipe TODO // void init_log_pipe() { // log_fd = open(LOG_FIFO_NAME.c_str(), O_WRONLY); // } // Returns file descriptor for server socket int start_socket() { int option = 1; int server_socket = socket(AF_INET, SOCK_STREAM, 0); setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); 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; } int status = listen(server_socket, MAX_CONNECTIONS); if (status != 0) { log("Error: can't listen with socket"); return -1; } 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 info1 = ""; std::string info2 = ""; log("New thread created for socket " + std::to_string(client_socket) + '\0'); while (true) { // Recieve message from client char buffer[128]; ssize_t bytes_recieved = recv(client_socket, buffer, sizeof(buffer) - 1, 0); buffer[bytes_recieved] = '\0'; // If connection closed if (bytes_recieved == 0) { log("Connection with client " + std::to_string(client_socket) + " closed"); pthread_mutex_lock(&conn_mutex); active_connections--; pthread_mutex_unlock(&conn_mutex); break; } if (bytes_recieved == -1) { log("Error while recieving message: " + std::to_string(errno) + " - " + std::strerror(errno) + '\0'); break; } log("Got new message from client " + std::to_string(client_socket) + '\0'); std::string new_info, req = std::string(buffer); log(req + '\0'); if (SERVER_TYPE == "server1") { if (req == "last_error") { new_info = get_last_error(); if (new_info == info1) { log("Actual info already on client\0"); continue; } info1 = new_info; } else if (req == "cursor_position") { new_info = get_cursor_position(); if (new_info == info2) { log("Actual info already on client\0"); continue; } info2 = new_info; } else { new_info = "Invalid request. Available options: last_error, cursor_position"; } } if (SERVER_TYPE == "server2") { if (req == "mem") { new_info = get_mem_percent(); if (new_info == info1) { log("Actual info already on client\0"); continue; } info1 = new_info; } else if (req == "vmem") { new_info = get_vmem(); if (new_info == info2) { log("Actual info already on client\0"); continue; } info2 = new_info; } else { new_info = "Invalid request. Available options: mem, vmem"; } } 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 + new_info; send(client_socket, msg.c_str(), strlen(msg.c_str()) + 1, 0); log("Sent new info to client\0"); } close(client_socket); return nullptr; } // Refreshes console output void* draw(void* arg) { while (true) { // Update info clear(); printw("Use q + Enter to quit\n\n"); // Print configuration printw( "LOG_FIFO_NAME=%s\nSERVER_TYPE=%s\nPORT=%i\nCMD_LOG_BUFFER=%i\nCMD_REFRESH_TIME=%i\n\n", LOG_FIFO_NAME.c_str(), SERVER_TYPE.c_str(), PORT, CMD_LOG_BUFFER, CMD_REFRESH_TIME ); if (is_log_ok) { printw("Log server connected\n\n"); } else { printw("Log server is not connected\n\n"); } // Print statistics printw("Active connections: %i\n", active_connections); // 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)); } } // Read commands from screen void* commands(void* arg) { // Cast server socket descriptor int server_socket = *(int*)arg; delete (int*)arg; while (true) { std::string command; std::cin >> command; if (command == "quit" || command == "exit" || command == "q") { endwin(); close(server_socket); kill(0, 15); } } } int main() { init_ncurses(); // 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(); if (server_socket == -1) { getch(); endwin(); return 3; } log("Socket created\0"); // Create thread to read commands pthread_t tcid; int* server_socket_copy = new int(server_socket); if (pthread_create(&tcid, nullptr, &commands, server_socket_copy) != 0) { std::cout << ("Failed to create commands thread") << std::endl; endwin(); return 2; } pthread_detach(tcid); while (true) { int client_socket = accept(server_socket, nullptr, nullptr); if (client_socket == -1) { continue; } pthread_mutex_lock(&conn_mutex); active_connections++; pthread_mutex_unlock(&conn_mutex); // 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) { delete client_socket_copy; } pthread_detach(tid); } return 0; }