init
This commit is contained in:
commit
133d960919
Binary file not shown.
|
@ -0,0 +1,173 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
g++ -o client client.cpp
|
||||||
|
./client
|
|
@ -0,0 +1,7 @@
|
||||||
|
services:
|
||||||
|
log:
|
||||||
|
build:
|
||||||
|
context: ./log
|
||||||
|
volumes:
|
||||||
|
- &log_data ./log_data:/log_data
|
||||||
|
- &tmp ./tmp:/pipe
|
|
@ -0,0 +1,5 @@
|
||||||
|
FROM frolvlad/alpine-gxx
|
||||||
|
COPY . /app
|
||||||
|
WORKDIR /app
|
||||||
|
RUN g++ -o log log.cpp
|
||||||
|
CMD ["./log"]
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
g++ -o log log.cpp
|
||||||
|
./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
|
|
@ -0,0 +1,2 @@
|
||||||
|
g++ -o server server.cpp -lncurses
|
||||||
|
./server
|
Binary file not shown.
|
@ -0,0 +1,323 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <regex>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
g++ -o server server.cpp -lncurses
|
||||||
|
./server
|
Binary file not shown.
|
@ -0,0 +1,323 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <regex>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
Loading…
Reference in New Issue