2022-09-14 10:07:25 +03:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2022-09-16 20:32:53 +03:00
|
|
|
#include <string.h>
|
2022-09-14 10:07:25 +03:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <unistd.h>
|
2022-09-16 20:32:53 +03:00
|
|
|
#include <stdint.h>
|
2022-09-14 10:07:25 +03:00
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
#define MAX_CHANNELS 128
|
|
|
|
#define MAX_CLIENTS 128
|
|
|
|
#define MAX_CLIENTS_PER_CHANNEL 64
|
|
|
|
#define CHANNEL_NAME_LENGTH 28
|
2022-09-14 10:07:25 +03:00
|
|
|
#define BACKLOG 10
|
|
|
|
#define DIE(info) ({perror((info)); exit(EXIT_FAILURE);})
|
|
|
|
|
|
|
|
static const char *socket_path = "/tmp/plumb-unix0";
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
struct client {
|
2022-09-14 10:07:25 +03:00
|
|
|
int fd;
|
|
|
|
};
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
struct channel {
|
|
|
|
char name[CHANNEL_NAME_LENGTH];
|
|
|
|
int clients[MAX_CLIENTS_PER_CHANNEL];
|
|
|
|
int clients_count;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct server {
|
|
|
|
int sock_fd;
|
2022-09-14 10:07:25 +03:00
|
|
|
int fd_num;
|
2022-09-16 20:32:53 +03:00
|
|
|
struct channel channels[MAX_CHANNELS];
|
|
|
|
struct client clients[MAX_CLIENTS];
|
2022-09-14 10:07:25 +03:00
|
|
|
struct pollfd fds[MAX_CLIENTS + 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
// https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function#FNV-1a_hash
|
|
|
|
uint64_t hashmap_hash(const char *bytes, size_t bytes_n, size_t map_len)
|
2022-09-14 10:07:25 +03:00
|
|
|
{
|
2022-09-16 20:32:53 +03:00
|
|
|
uint64_t hash = 0xcbf29ce484222325;
|
2022-09-14 10:07:25 +03:00
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
for (size_t i = 0; i < bytes_n; i++) {
|
|
|
|
hash *= 0x100000001b3;
|
|
|
|
hash ^= bytes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return (hash % map_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int server_client_add(struct server *s, int fd)
|
|
|
|
{
|
2022-09-14 10:07:25 +03:00
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
if (s->clients[i].fd < 0) {
|
|
|
|
s->clients[i].fd = fd;
|
|
|
|
s->fds[i].fd = fd;
|
|
|
|
s->fds[i].events = POLLIN;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
void server_client_remove(struct server *s, int i)
|
2022-09-14 10:07:25 +03:00
|
|
|
{
|
|
|
|
if (s->clients[i].fd >= 0) {
|
|
|
|
close(s->clients[i].fd);
|
|
|
|
}
|
|
|
|
s->clients[i].fd = -1;
|
|
|
|
s->fds[i].fd = -1;
|
|
|
|
s->fds[i].events = 0;
|
|
|
|
s->fds[i].revents = 0;
|
2022-09-16 20:32:53 +03:00
|
|
|
|
|
|
|
for (int channel = 0; channel < MAX_CHANNELS; channel++) {
|
|
|
|
for (int client = 0; client < MAX_CLIENTS_PER_CHANNEL; client++) {
|
|
|
|
if (s->channels[channel].clients[client] == i) {
|
|
|
|
s->channels[channel].clients[client] = -1;
|
|
|
|
s->channels[channel].clients_count--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void server_client_error(struct server *s, int i, const char *msg)
|
|
|
|
{
|
|
|
|
send(s->clients[i].fd, msg, strlen(msg), 0);
|
|
|
|
server_client_remove(s, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_channel_create(struct server *s, const char name[CHANNEL_NAME_LENGTH])
|
|
|
|
{
|
|
|
|
uint64_t bucket = hashmap_hash(name, CHANNEL_NAME_LENGTH, MAX_CHANNELS);
|
|
|
|
|
|
|
|
for (uint64_t i = bucket; i < bucket + 12 && i < MAX_CHANNELS; i++) {
|
|
|
|
if (s->channels[i].clients_count == 0) {
|
|
|
|
strncpy(s->channels[i].name, name, CHANNEL_NAME_LENGTH);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_channel_find(struct server *s, const char name[CHANNEL_NAME_LENGTH])
|
|
|
|
{
|
|
|
|
uint64_t bucket = hashmap_hash(name, CHANNEL_NAME_LENGTH, MAX_CHANNELS);
|
|
|
|
|
|
|
|
for (uint64_t i = bucket; i < bucket + 12 && i < MAX_CHANNELS; i++) {
|
|
|
|
if (strncmp(s->channels[i].name, name, CHANNEL_NAME_LENGTH) == 0) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_channel_find_or_create(struct server *s, const char name[CHANNEL_NAME_LENGTH])
|
|
|
|
{
|
|
|
|
int found = server_channel_find(s, name);
|
|
|
|
return found == -1 ? server_channel_create(s, name) : found;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_channel_add_client(struct server *s, int channel_index, int client_index)
|
|
|
|
{
|
|
|
|
int *clients = s->channels[channel_index].clients;
|
|
|
|
for (int i = 0; i < MAX_CLIENTS_PER_CHANNEL; i++) {
|
|
|
|
if (clients[i] == client_index) {
|
|
|
|
return -1;
|
|
|
|
} else if (clients[i] == -1) { // empty slot
|
|
|
|
clients[i] = client_index;
|
|
|
|
s->channels[channel_index].clients_count++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_channel_remove_client(struct server *s, int channel_index, int client_index)
|
|
|
|
{
|
|
|
|
int *clients = s->channels[channel_index].clients;
|
|
|
|
for (int i = 0; i < MAX_CLIENTS_PER_CHANNEL; i++) {
|
|
|
|
if (clients[i] == client_index) {
|
|
|
|
clients[i] = -1;
|
|
|
|
s->channels[channel_index].clients_count--;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2022-09-14 10:07:25 +03:00
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
int server_channel_send(struct server *s, int ci, const void *buf, size_t buf_size) {
|
|
|
|
int *clients = s->channels[ci].clients;
|
|
|
|
int remaining = s->channels[ci].clients_count;
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_CLIENTS_PER_CHANNEL; i++) {
|
|
|
|
if (remaining <= 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (clients[i] >= 0) {
|
|
|
|
send(s->clients[clients[i]].fd, buf, buf_size, 0);
|
|
|
|
remaining--;
|
|
|
|
}
|
|
|
|
}
|
2022-09-14 10:07:25 +03:00
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void server_free(struct server *s)
|
2022-09-14 10:07:25 +03:00
|
|
|
{
|
|
|
|
if (!s) return;
|
2022-09-16 20:32:53 +03:00
|
|
|
if (s->sock_fd) close(s->sock_fd);
|
2022-09-14 10:07:25 +03:00
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
struct server *server_create()
|
2022-09-14 10:07:25 +03:00
|
|
|
{
|
2022-09-16 20:32:53 +03:00
|
|
|
struct server *s = malloc(sizeof(struct server));
|
2022-09-14 10:07:25 +03:00
|
|
|
if (s == NULL) {
|
|
|
|
DIE("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
s->sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (s->sock_fd == -1) {
|
2022-09-14 10:07:25 +03:00
|
|
|
free(s);
|
|
|
|
DIE("socket");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sockaddr_un name;
|
|
|
|
memset(&name, 0, sizeof(name));
|
|
|
|
name.sun_family = AF_UNIX;
|
|
|
|
strncpy(name.sun_path, socket_path, sizeof(name.sun_path) - 1);
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
if (bind(s->sock_fd, (const struct sockaddr *)&name, sizeof(name)) == -1) {
|
2022-09-14 10:07:25 +03:00
|
|
|
free(s);
|
2022-09-16 20:32:53 +03:00
|
|
|
close(s->sock_fd);
|
2022-09-14 10:07:25 +03:00
|
|
|
DIE("bind");
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-09-16 20:32:53 +03:00
|
|
|
if (listen(s->sock_fd, BACKLOG) == -1) {
|
2022-09-14 10:07:25 +03:00
|
|
|
free(s);
|
2022-09-16 20:32:53 +03:00
|
|
|
close(s->sock_fd);
|
2022-09-14 10:07:25 +03:00
|
|
|
DIE("listen");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
|
|
|
s->clients[i].fd = -1;
|
|
|
|
s->fds[i].fd = -1;
|
|
|
|
s->fds[i].events = 0;
|
|
|
|
s->fds[i].revents = 0;
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
for (int i = 0; i < MAX_CHANNELS; i++) {
|
|
|
|
memset(s->channels[i].name, 0, sizeof(s->channels[i].name));
|
|
|
|
memset(s->channels[i].clients, -1, sizeof(s->channels[i].clients));
|
|
|
|
s->channels[i].clients_count = 0;
|
|
|
|
}
|
2022-09-14 10:07:25 +03:00
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
s->fds[MAX_CLIENTS].fd = s->sock_fd;
|
|
|
|
s->fds[MAX_CLIENTS].events = POLLIN;
|
2022-09-14 10:07:25 +03:00
|
|
|
s->fd_num = MAX_CLIENTS + 1;
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
int server_turn(struct server *s)
|
2022-09-14 10:07:25 +03:00
|
|
|
{
|
|
|
|
if (poll(s->fds, s->fd_num, -1) < 0) {
|
|
|
|
DIE("poll");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
#ifndef _client_check
|
|
|
|
#define _client_check(expr, msg) ({ if((expr)){server_client_error((s),(i),(msg));continue;} })
|
|
|
|
#endif
|
|
|
|
|
2022-09-14 10:07:25 +03:00
|
|
|
for (int i = 0; i < s->fd_num; i++) {
|
|
|
|
if (s->fds[i].revents & POLLIN) { // file descriptor ready for reading
|
2022-09-16 20:32:53 +03:00
|
|
|
if (s->fds[i].fd == s->sock_fd) { // new connection
|
|
|
|
int fd = accept(s->sock_fd, NULL, NULL);
|
2022-09-14 10:07:25 +03:00
|
|
|
if (fd == -1) {
|
|
|
|
DIE("accept");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
if (server_client_add(s, fd) == -1) {
|
2022-09-14 10:07:25 +03:00
|
|
|
close(fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else { // data from socket
|
|
|
|
char data[256];
|
|
|
|
memset(data, 0, sizeof(data));
|
|
|
|
ssize_t bytes = recv(s->fds[i].fd, data, sizeof(data), 0);
|
|
|
|
if (bytes == -1) {
|
|
|
|
DIE("recv");
|
|
|
|
return -1;
|
|
|
|
} else if (bytes == 0) { // client disconnected
|
2022-09-16 20:32:53 +03:00
|
|
|
server_client_remove(s, i);
|
2022-09-14 10:07:25 +03:00
|
|
|
} else {
|
2022-09-16 20:32:53 +03:00
|
|
|
const char *command = strtok(data, " ");
|
|
|
|
if (strcmp(command, "listen") == 0) {
|
|
|
|
char channel_name[CHANNEL_NAME_LENGTH];
|
|
|
|
strncpy(channel_name, strtok(NULL, " "), CHANNEL_NAME_LENGTH);
|
|
|
|
_client_check(*channel_name == '\0', "Invalid channel name");
|
|
|
|
|
|
|
|
int channel_id = server_channel_find_or_create(s, channel_name);
|
|
|
|
_client_check(channel_id < 0, "Could not find or create channel");
|
|
|
|
|
|
|
|
_client_check(server_channel_add_client(s, channel_id, i) < 0, "Could not join channel");
|
|
|
|
} else if (strcmp(command, "send") == 0) {
|
|
|
|
char channel_name[CHANNEL_NAME_LENGTH];
|
|
|
|
strncpy(channel_name, strtok(NULL, " "), CHANNEL_NAME_LENGTH);
|
|
|
|
_client_check(*channel_name == '\0', "Invalid channel name");
|
|
|
|
|
|
|
|
const char *payload = strtok(NULL, " ");
|
|
|
|
_client_check(payload == NULL || *channel_name == '\0', "Invalid channel name");
|
|
|
|
|
|
|
|
int channel_id = server_channel_find(s, channel_name);
|
|
|
|
_client_check(channel_id < 0, "Could not find channel");
|
|
|
|
|
|
|
|
_client_check(server_channel_send(s, channel_id, payload, strlen(payload)) < 0, "Could not send message");
|
|
|
|
} else {
|
|
|
|
server_client_error(s, i, "Unknown command");
|
|
|
|
continue;
|
|
|
|
}
|
2022-09-14 10:07:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
#ifdef _client_check
|
|
|
|
#undef _client_check
|
|
|
|
#endif
|
|
|
|
|
2022-09-14 10:07:25 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
2022-09-16 20:32:53 +03:00
|
|
|
struct server *srv = server_create();
|
2022-09-14 10:07:25 +03:00
|
|
|
if (srv == NULL) {
|
2022-09-16 20:32:53 +03:00
|
|
|
fprintf(stderr, "server_create failed\n");
|
2022-09-14 10:07:25 +03:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
2022-09-16 20:32:53 +03:00
|
|
|
if (server_turn(srv) < 0) {
|
|
|
|
fprintf(stderr, "server_turn failed\n");
|
|
|
|
server_free(srv);
|
2022-09-14 10:07:25 +03:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-16 20:32:53 +03:00
|
|
|
server_free(srv);
|
2022-09-14 10:07:25 +03:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|