added shared memory with support for a single client

This commit is contained in:
2026-06-10 17:42:30 +02:00
parent 0c3c1cbcc5
commit 924065e2ad
18 changed files with 342 additions and 7 deletions
+2
View File
@@ -21,3 +21,5 @@ $ cd client && make run
type `.help` for an overview of commands.
That's it!
##
+2 -2
View File
@@ -9,12 +9,12 @@ typedef enum BackendType {
MQ=0,
UDS=1,
TCP=2,
SHM=3,
} backend_type_t;
typedef struct Client {
backend_t backends[3];
backend_t backends[4];
backend_type_t selected_backend;
} client_t;
+3
View File
@@ -0,0 +1,3 @@
#pragma once
int shm_request(char* in, char* out);
+11 -2
View File
@@ -4,6 +4,7 @@
#include "mq.h"
#include "tcp.h"
#include "uds.h"
#include "shm.h"
#include <readline/history.h>
#include <readline/readline.h>
#include <stdio.h>
@@ -16,6 +17,7 @@ client_t client_create(void) {
mq_request,
uds_request,
tcp_request,
shm_request,
},
.selected_backend=TCP,
};
@@ -71,7 +73,10 @@ int client_parse_and_execute(client_t* client) {
client_select_backend(client, MQ);
return 0;
}
if (!strcmp(line, PROMPT_C "ishm")) {
client_select_backend(client, SHM);
return 0;
}
if (!strcmp(line, PROMPT_C "clear") || !strcmp(line, PROMPT_C "c")) {
system("clear");
return 0;
@@ -109,6 +114,10 @@ char* get_backend_name(backend_type_t type) {
return "MQ";
case TCP:
return "TCP";
default: return NULL;
case SHM:
return "SHM";
default:
log_warn_m("Backend name string not defined!");
return NULL;
}
}
+1
View File
@@ -33,6 +33,7 @@ const char* get_op_help_str() {
PROMPT_C "itcp | :: set IPC backend to " C_YELLOW "'TCP'" C_GREEN "\n"
PROMPT_C "iuds | :: set IPC backend to " C_YELLOW "'UDS'" C_GREEN "\n"
PROMPT_C "imq | :: set IPC backend to " C_YELLOW "'MQ'" C_GREEN "\n"
PROMPT_C "ishm | :: set IPC backend to " C_YELLOW "'SHM'" C_GREEN "\n"
PROMPT_C "clear | " PROMPT_C "c :: clear screen" "\n"
PROMPT_C "exit | " PROMPT_C "e :: close interactive shell" "\n" "\n"
"|>Work in progress:"
+92
View File
@@ -0,0 +1,92 @@
#include "shm.h"
#include "protocol.h"
#include "log.h"
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAX_RESP_WAIT_TIME_ms (1000*1000) // 1s
void close_and_cleanup(shared_memory_buf_t* buffer, int fd) {
atomic_store(&buffer->shared_status, SHM_STAT_DONE);
munmap(buffer, sizeof(shared_memory_buf_t));
close(fd);
}
int shm_request(char* in, char* out) {
// bind shared memory
int fd = shm_open(SHM_PATH, O_RDWR, 0664);
int errn = errno;
if (fd < 0) {
log_err_m("Opening shared memory failed with (err: %s). Is the server running?", strerror(errn));
return 1;
}
shared_memory_buf_t *buf = mmap(NULL, sizeof(shared_memory_buf_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
log_err_m("Mapping shared memory failed with (err: %s). Is the server running?", strerror(errn));
return 1;
}
const long retry_delay_ms = 200;
bool client_free = false;
for (int i = 0; i < MAX_RESP_WAIT_TIME_ms; i += retry_delay_ms) {
if (buf->shared_status == SHM_STAT_AVAIL) {
// increment status
atomic_store(&buf->shared_status, SHM_STAT_NEW_CONNECT);
client_free=true;
break;
};
usleep(retry_delay_ms);
}
if (!client_free) {
log_err_m("Timeout: Server is busy and SHM does not support multiple clients!");
return 1;
}
// <== connected ==>
// now write
buf->message = (mq_message_t){
.request_id=67,
.client_pid = getpid(),
};
memcpy(buf->message.payload, in, READ_BUFFER_SIZE);
// and set flag
atomic_store(&buf->shared_status, SHM_STAT_REQ_WRITTEN);
// now wait for response
bool responded = false;
for (int i = 0; i < MAX_RESP_WAIT_TIME_ms; i += retry_delay_ms) {
if (buf->shared_status == SHM_STAT_RES_WRITTEN) {
//
responded=true;
break;
};
usleep(retry_delay_ms);
}
if (!responded) {
log_err_m("Timeout: Server didn't respond!");
close_and_cleanup(buf, fd);
return 1;
}
// read
memcpy(out, buf->response.payload, WRITE_BUFFER_SIZE);
// done
close_and_cleanup(buf, fd);
return 0;
}
+1 -1
View File
@@ -34,7 +34,7 @@ cloc:
@cloc --exclude-ext=d,json --fullpath --not-match-d='(.*)deps(.*)' .
monitor:
@watch -t -n 0.1 "ls /dev/mqueue/ /tmp | grep -E 'store|:' && echo tcp: && ss -tlnp | grep store | awk '{printf \"%s --> %s\\n\",$$5, $$6;}'"
@watch -t -n 0.1 "ls /dev/mqueue/ /tmp /dev/shm | grep -E 'store|:' && echo tcp: && ss -tlnp | grep store"
-include $(DEP_FILES)
.PHONY: all clean re run cloc monitor
+2
View File
@@ -1,5 +1,6 @@
#pragma once
#include "shm_client.h"
#include "tcp_client.h"
#include "uds_client.h"
#include "mq_client.h"
@@ -34,6 +35,7 @@ typedef union ClientData {
uds_client_data_t uds_data;
mq_client_data_t mq_data;
tcp_client_data_t tcp_data;
shm_client_data_t shm_data;
} client_data_t;
typedef struct Client client_t;
+2
View File
@@ -3,6 +3,7 @@
#include "tcp_conn_handle.h"
#include "uds_conn_handle.h"
#include "mq_conn_handle.h"
#include "shm_conn_handle.h"
typedef struct ConnectionResult {
enum { CONN_NONE, CONN_ERR, CONN_NEW } status;
@@ -16,6 +17,7 @@ typedef union ConnHandlData {
uds_handl_data_t uds_data;
mq_handl_data_t mq_data;
tcp_handl_data_t tcp_data;
shm_handl_data_t shm_data;
} conn_handl_data_t;
typedef struct ConnectionHandler connection_handler_t;
+1 -1
View File
@@ -11,7 +11,7 @@
typedef struct Server {
volatile int running;
connection_handler_t conn_handlers[3];
connection_handler_t conn_handlers[4];
size_t num_conn_handlers;
database_t* db;
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "protocol.h"
#include <stdlib.h>
#include <stdint.h>
// forward declare so no circular deps with client.h
typedef struct Client client_t;
typedef struct ClientReadResult client_read_result_t;
typedef struct ClientWriteResult client_write_result_t;
client_t shm_client_create(shared_memory_buf_t* buffer, int fd);
typedef struct SHMClientdata {
int fd;
shared_memory_buf_t* buffer;
} shm_client_data_t;
// api functs
client_read_result_t shm_client_read(client_t* client);
client_write_result_t shm_client_write(client_t* client, uint8_t* data, size_t data_size);
void shm_client_close(client_t* client);
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "protocol.h"
typedef struct ConnectionHandler connection_handler_t;
typedef struct ConnectionResult connection_result_t;
typedef struct SHMHandldata {
int fd;
shared_memory_buf_t* buffer;
} shm_handl_data_t;
connection_handler_t create_shm_handler(void);
int shm_init(connection_handler_t*);
connection_result_t shm_check_conn(connection_handler_t*);
void shm_cleanup(connection_handler_t*);
+1 -1
View File
@@ -4,8 +4,8 @@
#include "connection_handler.h"
#include "mq_conn_handle.h"
#include "client.h"
#include "shm_client.h"
#include "client_thread.h"
#include "log.h"
connection_handler_t create_mq_handler(void) {
+2
View File
@@ -5,6 +5,7 @@
#include "server.h"
#include "db.h"
#include "connection_handler.h"
#include "shm_conn_handle.h"
#include "uds_conn_handle.h"
#include "mq_conn_handle.h"
@@ -20,6 +21,7 @@ server_t* server_create(void) {
create_uds_handler(),
create_mq_handler(),
create_tcp_handler(),
create_shm_handler(),
},
};
srv->num_conn_handlers = sizeof(srv->conn_handlers) / sizeof(connection_handler_t);
+60
View File
@@ -0,0 +1,60 @@
#include "shm_client.h"
#include "client.h"
#include "log.h"
#include "protocol.h"
#include <stdatomic.h>
#include <string.h>
client_t shm_client_create(shared_memory_buf_t* buffer, int fd) {
client_t client = {
.client_data.shm_data = (shm_client_data_t) {
.fd = fd,
.buffer = buffer,
},
.read = shm_client_read,
.write = shm_client_write,
.close = shm_client_close,
};
log_info_m("SHM client connected");
return client;
}
client_read_result_t shm_client_read(client_t* client) {
shared_memory_buf_t* buffer = client->client_data.shm_data.buffer;
while (buffer->shared_status != SHM_STAT_REQ_WRITTEN) { };
// read
client_read_result_t result = {
.status = READ_OK,
.len = READ_BUFFER_SIZE,
};
memcpy(result.data, &buffer->message.payload, READ_BUFFER_SIZE);
return result;
}
client_write_result_t shm_client_write(client_t* client, uint8_t* data, size_t data_size) {
shared_memory_buf_t* buffer = client->client_data.shm_data.buffer;
memcpy(&buffer->response.payload, data, data_size);
atomic_store(&buffer->shared_status, SHM_STAT_RES_WRITTEN);
return (client_write_result_t) {
.status = WRITE_OK
};
}
void shm_client_close(client_t* client) {
shared_memory_buf_t* buffer = client->client_data.shm_data.buffer;
while (buffer->shared_status != SHM_STAT_DONE) { };
memset(buffer, 0, sizeof(shared_memory_buf_t));
atomic_store(&buffer->shared_status, SHM_STAT_AVAIL);
}
+93
View File
@@ -0,0 +1,93 @@
#include "shm_conn_handle.h"
#include "client.h"
#include "shm_client.h"
#include "client_thread.h"
#include "connection_handler.h"
#include "log.h"
#include "protocol.h"
#include <errno.h>
#include <stdatomic.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
connection_handler_t create_shm_handler(void) {
return (connection_handler_t) {
.conn_data.shm_data = (shm_handl_data_t) {
.fd = -1,
},
.init = shm_init,
.check_conn = shm_check_conn,
.cleanup = shm_cleanup,
};
}
int shm_init(connection_handler_t* handle) {
// initialize shared memory
int fd = shm_open(SHM_PATH, O_NONBLOCK | O_RDWR | O_CREAT, 0664);
int errn = errno;
if (fd < 0) {
log_err_m("Opening shared memory failed with (err: %s)", strerror(errn));
return 1;
}
int err = ftruncate(fd, sizeof(shared_memory_buf_t));
errn = errno;
if (err < 0) {
log_err_m("Truncating shared memory failed with (err: %s)", strerror(errn));
return 1;
}
shared_memory_buf_t* buffer = mmap(NULL, sizeof(shared_memory_buf_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
errn = errno;
if (buffer == MAP_FAILED) {
log_err_m("Mapping shared memory failed with (err: %s)", strerror(errn));
return 1;
}
handle->conn_data.shm_data.fd = fd;
handle->conn_data.shm_data.buffer = buffer;
memset(buffer, 0, sizeof(shared_memory_buf_t));
atomic_store(&buffer->shared_status, SHM_STAT_AVAIL);
log_info_m("Initiaized SHM buffer");
return 0;
}
connection_result_t shm_check_conn(connection_handler_t* handl) {
shared_memory_buf_t* shm_buffer = handl->conn_data.shm_data.buffer;
shm_status_t status = shm_buffer->shared_status;
if (status != SHM_STAT_NEW_CONNECT && status != SHM_STAT_REQ_WRITTEN) {
return (connection_result_t) {
.status = CONN_NONE,
};
};
log_ok_m("New client connected via MessageQueue!");
// create client object and hand to thread
client_t new_client = shm_client_create(shm_buffer, handl->conn_data.shm_data.fd);
pthread_t tid = client_thread_start(new_client);
return (connection_result_t) {
.status = CONN_NEW,
.tid = tid,
};
}
void shm_cleanup(connection_handler_t* handl) {
(void) handl;
munmap(handl->conn_data.shm_data.buffer, sizeof(shared_memory_buf_t));
close(handl->conn_data.shm_data.fd);
shm_unlink(SHM_PATH);
}
+1
View File
@@ -18,6 +18,7 @@ static inline void set_log_level(char level) {
#define LL_INFO 0b00100000
#define LL_OK 0b00010000
#define LL_ALL 0b11110000
#define LL_NONE 0
#define C_NORMAL "\033[0m"
+28
View File
@@ -1,5 +1,6 @@
#pragma once
#include <stdatomic.h>
#include <stdint.h>
#include <sys/types.h>
@@ -20,6 +21,8 @@
#define TCP_PORT 6767
#define SHM_PATH "/storebus"
// message for shared incoming queue
typedef struct MQMessage {
pid_t client_pid;
@@ -32,3 +35,28 @@ typedef struct MQResponse {
unsigned int request_id;
uint8_t payload[MQ_PAYLOAD_SIZE_OUT];
} mq_response_t;
// shared memory
/*
* 0 = availible for new connections
* 1 = connection reserved
* 2 = client wrote request
* 3 = server wrote response
* 4 = client finished
*/
typedef enum SHMStatus {
SHM_STAT_AVAIL = 0,
SHM_STAT_NEW_CONNECT = 1,
SHM_STAT_REQ_WRITTEN = 2,
SHM_STAT_RES_WRITTEN = 3,
SHM_STAT_DONE = 4,
} shm_status_t;
typedef struct SharedMemoryBuf {
_Atomic shm_status_t shared_status;
mq_message_t message;
mq_response_t response;
} shared_memory_buf_t ;