#include "bits/types/struct_timeval.h" #include "sys/ipc.h" #include "sys/shm.h" #include #include #include #include #include #include #include #include #include #include #include "commands.h" #define send(queue_id, msgbuf_ptr) \ msgsnd(queue_id, msgbuf_ptr, sizeof(*(msgbuf_ptr)) - sizeof(long), 0) typedef struct { char *id; bool logged_in; char *muted_clients; char nickname[16]; int queue_id; long last_seen; } Client; typedef struct { Client **clients; char name[16]; int client_count; } Group; static void handle_hearbeat_timeouts(Client *clients, int client_count, int semaphore_id) { struct sembuf sem_op = { .sem_num = 0, .sem_op = 1, .sem_flg = 0, }; while (1) { sem_op.sem_op = 1; semop(semaphore_id, &sem_op, 1); struct timeval tv; gettimeofday(&tv, NULL); for (int i = 0; i < client_count; i++) { if (clients[i].logged_in && (tv.tv_sec - clients[i].last_seen) > HEARTBEAT_TIMEOUT) { printf("Client %s has timed out due to missed heartbeats\n", clients[i].id); clients[i].logged_in = false; clients[i].queue_id = -1; } } sem_op.sem_op = -1; semop(semaphore_id, &sem_op, 1); sleep(HEARTBEAT_TIMEOUT); } } int main(int argc, char **argv) { // TODO: Load config file with clients and groups int client_count = 2; int group_count = 3; int clients_memory_id = shmget(IPC_PRIVATE, sizeof(Client) * client_count, IPC_CREAT | 0666); Client *clients = shmat(clients_memory_id, NULL, 0); Group *groups = malloc(sizeof(Group) * 3); int semaphore_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666); semctl(semaphore_id, 0, SETVAL, 1); clients[0] = (Client){.id = "test1", .logged_in = false, .muted_clients = NULL, .nickname = "Kregielnia", .last_seen = 0}; clients[1] = (Client){.id = "test2", .logged_in = false, .muted_clients = NULL, .nickname = "Bajzel", .last_seen = 0}; int server_queue_id = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); if (server_queue_id == -1) { perror("msgget failed"); return 1; } int id_file_handle = open("/home/piotr/server_queue_id", O_WRONLY | O_CREAT | O_TRUNC, 0666); if (!id_file_handle) { perror("Failed to open file"); return 1; } if (write(id_file_handle, &server_queue_id, sizeof(int)) == -1) { perror("Failed to write server queue id to file"); return 1; } close(id_file_handle); if (fork() == 0) { handle_hearbeat_timeouts(clients, client_count, semaphore_id); return 0; } msgbuf_t msgbuf; while (1) { // Read from the ipc, then handle the command int read_status = msgrcv(server_queue_id, &msgbuf, sizeof(msgbuf) - sizeof(long), 0, 0); switch (msgbuf.mtype) { case Login: printf("Received login request for id: %s\n", msgbuf.sender); Client *client = NULL; for (int i = 0; i < 1; i++) { if (strncmp(clients[i].id, msgbuf.sender, COMMAND_LENGTH) == 0) { client = &clients[i]; break; } } // Reuse the buffer to get the client's message queue id int msg_queue_id = atoi(msgbuf.command); if (client == NULL) { printf("User not found: %s\n", msgbuf.sender); msgbuf_t response = { .mtype = Login, .stype = ERR_USER_NOT_FOUND, }; send(msg_queue_id, &response); continue; } if (client->logged_in) { printf("User already logged in: %s\n", msgbuf.sender); msgbuf_t response = { .mtype = Login, .stype = ERR_ALREADY_LOGGED_IN, }; send(msg_queue_id, &response); continue; } client->logged_in = true; client->queue_id = msg_queue_id; struct timeval tv; gettimeofday(&tv, NULL); client->last_seen = tv.tv_sec; msgbuf_t response = { .mtype = Login, .stype = ACK_ACCEPTED, }; printf("User accepted: %s\n", msgbuf.sender); send(msg_queue_id, &response); break; case Message: printf("Recieved message, checking which client\n"); // Find the client that sent the message then forward it to all the // specified recipients client = NULL; for (int i = 0; i < client_count; i++) { if (strncmp(clients[i].id, msgbuf.sender, COMMAND_LENGTH) == 0) { client = &clients[i]; break; } } printf("Received message from client id %s: %s\n", msgbuf.sender, msgbuf.message); if (client == NULL || !client->logged_in) { // Client not found or not logged in printf("Client not found or not logged in"); msgbuf_t response = { .mtype = Message, .stype = ERR_NOT_LOGGED_IN, }; send(read_status, &response); continue; } printf("Forwarding message: %s\n", msgbuf.message); // TODO: Find the actual recipients, for now broadcast msgbuf_t forward_msg = { .mtype = Message, }; strncpy(forward_msg.message, msgbuf.message, MESSAGE_LENGTH); // Yes, the client sending the message also receives it for (int i = 0; i < client_count; i++) { if (clients[i].logged_in) { send(clients[i].queue_id, &forward_msg); } } forward_msg.stype = ACK_ACCEPTED; forward_msg.mtype = Signal; send(client->queue_id, &forward_msg); // TODO: Accepted and Delivered ack break; case Logout: case Hearbeat: for (int i = 0; i < client_count; i++) { if (strncmp(clients[i].id, msgbuf.sender, COMMAND_LENGTH) == 0 && clients[i].logged_in) { // Take the semaphore to update last_seen struct sembuf sem_op = { .sem_num = 0, .sem_op = -1, .sem_flg = 0, }; semop(semaphore_id, &sem_op, 1); struct timeval tv; gettimeofday(&tv, NULL); clients[i].last_seen = tv.tv_sec; // Free the semaphore sem_op.sem_op = 1; semop(semaphore_id, &sem_op, 1); } } break; case List_clients: break; case Mute: case Unmute: break; case Signal: // Signals are sent from server to client, not the other way around // so we ignore them here lol break; } } return 0; }