Some documentation in libipc.h, size_t for buffer sizes.
parent
c0d6404186
commit
8ffd0faba1
54
libipc.h
54
libipc.h
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Event types: the "t" parameter in "ipc_wait_event".
|
||||||
enum event_types {
|
enum event_types {
|
||||||
ERROR = 0 // A problem occured.
|
ERROR = 0 // A problem occured.
|
||||||
, CONNECTION = 1 // New user.
|
, CONNECTION = 1 // New user.
|
||||||
|
@ -23,27 +24,64 @@ enum cb_event_types {
|
||||||
, CB_IGNORE = 3 // The message should be ignored (protocol specific).
|
, CB_IGNORE = 3 // The message should be ignored (protocol specific).
|
||||||
};
|
};
|
||||||
|
|
||||||
int ipc_context_init (void** ptr);
|
int ipc_context_init (void** ptr); // Allocate memory for a context.
|
||||||
|
void ipc_context_deinit (void** ctx); // Free the context's memory.
|
||||||
|
void ipc_context_timer (void* ctx, int timer); // Change the timer (for timer events, in ms).
|
||||||
|
|
||||||
|
// Init (or connect to) a service. That's almost the same operation behind the scene.
|
||||||
int ipc_service_init (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len);
|
int ipc_service_init (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len);
|
||||||
int ipc_connect_service (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len);
|
int ipc_connect_service (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len);
|
||||||
void ipc_context_deinit (void** ctx);
|
|
||||||
int ipc_write (void* ctx, int servicefd, char* mcontent, uint32_t mlen);
|
// Write a message or schedule it.
|
||||||
int ipc_schedule (void* ctx, int servicefd, const char* mcontent, uint32_t mlen);
|
int ipc_write (void* ctx, int servicefd, const char* mcontent, size_t mlen);
|
||||||
|
int ipc_schedule (void* ctx, int servicefd, const char* mcontent, size_t mlen);
|
||||||
|
|
||||||
|
// Read a message from a client; either selected by an index in the context structure or by its file descriptor.
|
||||||
int ipc_read_fd (void* ctx, int fd, char* buffer, size_t* buflen);
|
int ipc_read_fd (void* ctx, int fd, char* buffer, size_t* buflen);
|
||||||
int ipc_read (void* ctx, size_t index, char* buffer, size_t* buflen);
|
int ipc_read (void* ctx, size_t index, char* buffer, size_t* buflen);
|
||||||
|
|
||||||
|
// Wait for an event.
|
||||||
|
// The "t" parameter is the type of the event (enum event_types).
|
||||||
|
// The "index" parameter is the index in the context structure of the origin of the event (client or server).
|
||||||
|
// The "originfd" parameter is the file descriptor on which the event occurs.
|
||||||
|
// The "newfd" parameter is the file descriptor of the new connected client, in case of a connection event.
|
||||||
|
// The "buffer" and "buflen" parameters contain respectively a copy of the received message and its length.
|
||||||
int ipc_wait_event (void* ctx, char* t, size_t* index, int* originfd, int* newfd, char* buffer, size_t* buflen);
|
int ipc_wait_event (void* ctx, char* t, size_t* index, int* originfd, int* newfd, char* buffer, size_t* buflen);
|
||||||
void ipc_context_timer (void* ctx, int timer);
|
|
||||||
|
// Close a client (or server) based on its file descriptor or its index in the context structure.
|
||||||
int ipc_close_fd (void* ctx, int fd);
|
int ipc_close_fd (void* ctx, int fd);
|
||||||
int ipc_close (void* ctx, size_t index);
|
int ipc_close (void* ctx, size_t index);
|
||||||
|
// Close all connections (probably right before the processus is terminated).
|
||||||
int ipc_close_all (void* ctx);
|
int ipc_close_all (void* ctx);
|
||||||
|
|
||||||
// Switch functions (for "protocol" services, such as TCPd).
|
// Add a (possibly non-IPC) file descriptor to handle.
|
||||||
|
// Since it's not marked as an IPC connection, messages won't be automatically read;
|
||||||
|
// which enables to handle any communications for most protocols through the LibIPC API.
|
||||||
int ipc_add_external (void* ctx, int newfd);
|
int ipc_add_external (void* ctx, int newfd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SWITCH: enables the automatic exchange of messages between a pair of file descriptors.
|
||||||
|
*
|
||||||
|
* There are two operations available:
|
||||||
|
* - ipc_add_switch: add a new switch;
|
||||||
|
* - ipc_set_switch_callbacks: set callbacks (IO operations) for a file descriptor,
|
||||||
|
* enabling specific processing for a connection.
|
||||||
|
*
|
||||||
|
* Callbacks enable to handle non-IPC communications, for example a websocket connection,
|
||||||
|
* a non-IPC client communicating with JSON messages (or any other format), etc.
|
||||||
|
*
|
||||||
|
* Switch and IO callbacks enable to easily create "protocol IPC services" (such as TCPd) to
|
||||||
|
* bind IPC services to basically any available protocol through small, dedicated services
|
||||||
|
* handling all the nitty-gritty details of non-IPC protocols.
|
||||||
|
*/
|
||||||
|
// Add a new switch between a pair of file descriptors, enabling automatic exchange of messages
|
||||||
|
// between this pair of fds. Useful for "protocol services", such as TCPd.
|
||||||
int ipc_add_switch (void* ctx, int fd1, int fd2);
|
int ipc_add_switch (void* ctx, int fd1, int fd2);
|
||||||
|
|
||||||
|
// Set IO callbacks for a file descriptor.
|
||||||
// Returned "char" is a cb_event_types enum.
|
// Returned "char" is a cb_event_types enum.
|
||||||
int ipc_set_switch_callbacks (void* ctx, int fd
|
int ipc_set_switch_callbacks (void* ctx, int fd
|
||||||
, char (*in (int orig, const char *payload, uint32_t *mlen))
|
, char (*in (int orig, char *payload, size_t *mlen))
|
||||||
, char (*out(int dest, char *payload, uint32_t mlen)));
|
, char (*out(int dest, const char *payload, size_t mlen)));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,7 +36,7 @@ export fn ipc_context_deinit(ctx: **Context) callconv(.C) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a message (no waiting).
|
/// Write a message (no waiting).
|
||||||
export fn ipc_write(ctx: *Context, servicefd: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) i32 {
|
export fn ipc_write(ctx: *Context, servicefd: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) i32 {
|
||||||
// TODO: better default length.
|
// TODO: better default length.
|
||||||
var buffer = [_]u8{0} ** 100000;
|
var buffer = [_]u8{0} ** 100000;
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||||
|
@ -48,7 +48,7 @@ export fn ipc_write(ctx: *Context, servicefd: i32, mcontent: [*]const u8, mlen:
|
||||||
|
|
||||||
/// Schedule a message.
|
/// Schedule a message.
|
||||||
/// Use the same allocator as the context.
|
/// Use the same allocator as the context.
|
||||||
export fn ipc_schedule(ctx: *Context, servicefd: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) i32 {
|
export fn ipc_schedule(ctx: *Context, servicefd: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) i32 {
|
||||||
const message = Message.init(servicefd, ctx.allocator, mcontent[0..mlen]) catch return -1;
|
const message = Message.init(servicefd, ctx.allocator, mcontent[0..mlen]) catch return -1;
|
||||||
ctx.schedule(message) catch return -2;
|
ctx.schedule(message) catch return -2;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -152,8 +152,8 @@ export fn ipc_add_switch(ctx: *Context, fd1: i32, fd2: i32) callconv(.C) i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ipc_set_switch_callbacks(ctx: *Context, fd: i32,
|
export fn ipc_set_switch_callbacks(ctx: *Context, fd: i32,
|
||||||
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8,
|
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8,
|
||||||
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8) callconv(.C) i32 {
|
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8) callconv(.C) i32 {
|
||||||
ctx.set_switch_callbacks(fd, in, out) catch return -1;
|
ctx.set_switch_callbacks(fd, in, out) catch return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,8 +317,8 @@ pub const Context = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_switch_callbacks(self: *Self, fd: i32,
|
pub fn set_switch_callbacks(self: *Self, fd: i32,
|
||||||
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8,
|
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8,
|
||||||
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8) !void {
|
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8) !void {
|
||||||
try self.switchdb.set_callbacks(fd, in, out);
|
try self.switchdb.set_callbacks(fd, in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,8 @@ pub const SwitchDB = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_callbacks(self: *Self, fd: i32,
|
pub fn set_callbacks(self: *Self, fd: i32,
|
||||||
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8,
|
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8,
|
||||||
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8) !void {
|
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8) !void {
|
||||||
var managedconnection = self.db.get(fd) orelse return error.unregisteredFD;
|
var managedconnection = self.db.get(fd) orelse return error.unregisteredFD;
|
||||||
managedconnection.in = in;
|
managedconnection.in = in;
|
||||||
managedconnection.out = out;
|
managedconnection.out = out;
|
||||||
|
@ -80,7 +80,7 @@ pub const SwitchDB = struct {
|
||||||
var managedconnection = self.db.get(fd) orelse return error.unregisteredFD;
|
var managedconnection = self.db.get(fd) orelse return error.unregisteredFD;
|
||||||
|
|
||||||
var buffer = [_]u8{0} ** 100000; // TODO: buffer size
|
var buffer = [_]u8{0} ** 100000; // TODO: buffer size
|
||||||
var message_size: u32 = @truncate(buffer.len);
|
var message_size: usize = buffer.len;
|
||||||
const r: CBEventType = @enumFromInt(managedconnection.in(fd, &buffer, &message_size));
|
const r: CBEventType = @enumFromInt(managedconnection.in(fd, &buffer, &message_size));
|
||||||
|
|
||||||
switch (r) {
|
switch (r) {
|
||||||
|
@ -124,7 +124,7 @@ pub const SwitchDB = struct {
|
||||||
_ = message.write(writer) catch return error.generic;
|
_ = message.write(writer) catch return error.generic;
|
||||||
const written = fbs.getWritten();
|
const written = fbs.getWritten();
|
||||||
|
|
||||||
const r: CBEventType = @enumFromInt(managedconnection.out(message.fd, written.ptr, @truncate(written.len)));
|
const r: CBEventType = @enumFromInt(managedconnection.out(message.fd, written.ptr, written.len));
|
||||||
|
|
||||||
switch (r) {
|
switch (r) {
|
||||||
// The message should be ignored (protocol specific).
|
// The message should be ignored (protocol specific).
|
||||||
|
@ -186,8 +186,8 @@ pub const SwitchDB = struct {
|
||||||
|
|
||||||
const ManagedConnection = struct {
|
const ManagedConnection = struct {
|
||||||
dest: i32,
|
dest: i32,
|
||||||
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8 = default_in,
|
in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8 = default_in,
|
||||||
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8 = default_out,
|
out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8 = default_out,
|
||||||
};
|
};
|
||||||
|
|
||||||
test "creation and display" {
|
test "creation and display" {
|
||||||
|
@ -205,18 +205,18 @@ test "creation and display" {
|
||||||
try print_eq("{ (5,6)(6,5) }", .{switchdb});
|
try print_eq("{ (5,6)(6,5) }", .{switchdb});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn successful_in(_: i32, mcontent: [*]u8, mlen: *u32) CBEventType {
|
fn successful_in(_: i32, mcontent: [*]u8, mlen: *usize) CBEventType {
|
||||||
var m = Message.init(8, std.heap.c_allocator, "coucou") catch unreachable;
|
var m = Message.init(8, std.heap.c_allocator, "coucou") catch unreachable;
|
||||||
defer m.deinit();
|
defer m.deinit();
|
||||||
|
|
||||||
var fbs = std.io.fixedBufferStream(mcontent[0..mlen.*]);
|
var fbs = std.io.fixedBufferStream(mcontent[0..mlen.*]);
|
||||||
const writer = fbs.writer();
|
const writer = fbs.writer();
|
||||||
const bytes_written = m.write(writer) catch unreachable;
|
const bytes_written = m.write(writer) catch unreachable;
|
||||||
mlen.* = @truncate(bytes_written);
|
mlen.* = bytes_written;
|
||||||
return CBEventType.NO_ERROR;
|
return CBEventType.NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn successful_out(_: i32, _: [*]const u8, _: u32) CBEventType {
|
fn successful_out(_: i32, _: [*]const u8, _: usize) CBEventType {
|
||||||
return CBEventType.NO_ERROR;
|
return CBEventType.NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,11 +255,11 @@ test "successful exchanges" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsuccessful_in(_: i32, _: [*]const u8, _: *u32) CBEventType {
|
fn unsuccessful_in(_: i32, _: [*]const u8, _: *usize) CBEventType {
|
||||||
return CBEventType.ERROR;
|
return CBEventType.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsuccessful_out(_: i32, _: [*]const u8, _: u32) CBEventType {
|
fn unsuccessful_out(_: i32, _: [*]const u8, _: usize) CBEventType {
|
||||||
return CBEventType.ERROR;
|
return CBEventType.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ test "nuke 'em" {
|
||||||
try testing.expect(switchdb.db.count() == 0);
|
try testing.expect(switchdb.db.count() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_in(origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8 {
|
fn default_in(origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8 {
|
||||||
// This may be kinda hacky, idk.
|
// This may be kinda hacky, idk.
|
||||||
var stream: net.Stream = .{ .handle = origin };
|
var stream: net.Stream = .{ .handle = origin };
|
||||||
const packet_size: usize = stream.read(mcontent[0..mlen.*]) catch return @intFromEnum(CBEventType.ERROR);
|
const packet_size: usize = stream.read(mcontent[0..mlen.*]) catch return @intFromEnum(CBEventType.ERROR);
|
||||||
|
@ -322,12 +322,12 @@ fn default_in(origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8 {
|
||||||
return @intFromEnum(CBEventType.FD_CLOSING);
|
return @intFromEnum(CBEventType.FD_CLOSING);
|
||||||
}
|
}
|
||||||
|
|
||||||
mlen.* = @truncate(packet_size);
|
mlen.* = packet_size;
|
||||||
|
|
||||||
return @intFromEnum(CBEventType.NO_ERROR);
|
return @intFromEnum(CBEventType.NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_out(fd: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8 {
|
fn default_out(fd: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8 {
|
||||||
// Message contains the fd, no need to search for the right structure to copy,
|
// Message contains the fd, no need to search for the right structure to copy,
|
||||||
// let's just recreate a Stream from the fd.
|
// let's just recreate a Stream from the fd.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue