diff --git a/libipc.h b/libipc.h index 0385ee2..20c72de 100644 --- a/libipc.h +++ b/libipc.h @@ -3,47 +3,85 @@ #include +// Event types: the "t" parameter in "ipc_wait_event". enum event_types { - ERROR = 0 // A problem occured. - , CONNECTION = 1 // New user. - , DISCONNECTION = 2 // User disconnected. - , MESSAGE_RX = 3 // New message. - , MESSAGE_TX = 4 // Message sent. - , TIMER = 5 // Timeout in the poll(2) function. - , EXTERNAL = 6 // Message received from a non IPC socket. - , SWITCH_RX = 7 // Message received from a switched FD. - , SWITCH_TX = 8 // Message sent to a switched fd. + ERROR = 0 // A problem occured. + , CONNECTION = 1 // New user. + , DISCONNECTION = 2 // User disconnected. + , MESSAGE_RX = 3 // New message. + , MESSAGE_TX = 4 // Message sent. + , TIMER = 5 // Timeout in the poll(2) function. + , EXTERNAL = 6 // Message received from a non IPC socket. + , SWITCH_RX = 7 // Message received from a switched FD. + , SWITCH_TX = 8 // Message sent to a switched fd. }; // Return type of callback functions when switching. enum cb_event_types { - CB_NO_ERROR = 0 // No error. A message was generated. - , CB_ERROR = 1 // Generic error. - , CB_FD_CLOSING = 2 // The fd is closing. - , CB_IGNORE = 3 // The message should be ignored (protocol specific). + CB_NO_ERROR = 0 // No error. A message was generated. + , CB_ERROR = 1 // Generic error. + , CB_FD_CLOSING = 2 // The fd is closing. + , CB_IGNORE = 3 // The message should be ignored (protocol specific). }; -int ipc_context_init (void** ptr); -int ipc_service_init (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len); +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_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); -int ipc_schedule (void* ctx, int servicefd, const char* mcontent, uint32_t mlen); -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_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); -int ipc_close_fd (void* ctx, int fd); -int ipc_close (void* ctx, size_t index); + +// Write a message or schedule it. +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 (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); + +// 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 (void* ctx, size_t index); +// Close all connections (probably right before the processus is terminated). 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); + +/** + * 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); +// Set IO callbacks for a file descriptor. // Returned "char" is a cb_event_types enum. int ipc_set_switch_callbacks (void* ctx, int fd - , char (*in (int orig, const char *payload, uint32_t *mlen)) - , char (*out(int dest, char *payload, uint32_t mlen))); + , char (*in (int orig, char *payload, size_t *mlen)) + , char (*out(int dest, const char *payload, size_t mlen))); #endif diff --git a/src/bindings.zig b/src/bindings.zig index 2b98e43..cca83bc 100644 --- a/src/bindings.zig +++ b/src/bindings.zig @@ -36,7 +36,7 @@ export fn ipc_context_deinit(ctx: **Context) callconv(.C) void { } /// 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. var buffer = [_]u8{0} ** 100000; 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. /// 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; ctx.schedule(message) catch return -2; 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, - in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8, - out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8) callconv(.C) i32 { + in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8, + 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; return 0; } diff --git a/src/context.zig b/src/context.zig index 5f6c29f..734c5ae 100644 --- a/src/context.zig +++ b/src/context.zig @@ -317,8 +317,8 @@ pub const Context = struct { } pub fn set_switch_callbacks(self: *Self, fd: i32, - in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8, - out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8) !void { + in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8, + out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8) !void { try self.switchdb.set_callbacks(fd, in, out); } diff --git a/src/switch.zig b/src/switch.zig index ec5a78a..203d310 100644 --- a/src/switch.zig +++ b/src/switch.zig @@ -65,8 +65,8 @@ pub const SwitchDB = struct { } pub fn set_callbacks(self: *Self, fd: i32, - in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8, - out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8) !void { + in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8, + out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8) !void { var managedconnection = self.db.get(fd) orelse return error.unregisteredFD; managedconnection.in = in; managedconnection.out = out; @@ -80,7 +80,7 @@ pub const SwitchDB = struct { var managedconnection = self.db.get(fd) orelse return error.unregisteredFD; 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)); switch (r) { @@ -124,7 +124,7 @@ pub const SwitchDB = struct { _ = message.write(writer) catch return error.generic; 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) { // The message should be ignored (protocol specific). @@ -186,8 +186,8 @@ pub const SwitchDB = struct { const ManagedConnection = struct { dest: i32, - in: *const fn (origin: i32, mcontent: [*]u8, mlen: *u32) callconv(.C) u8 = default_in, - out: *const fn (origin: i32, mcontent: [*]const u8, mlen: u32) callconv(.C) u8 = default_out, + in: *const fn (origin: i32, mcontent: [*]u8, mlen: *usize) callconv(.C) u8 = default_in, + out: *const fn (origin: i32, mcontent: [*]const u8, mlen: usize) callconv(.C) u8 = default_out, }; test "creation and display" { @@ -205,18 +205,18 @@ test "creation and display" { 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; defer m.deinit(); var fbs = std.io.fixedBufferStream(mcontent[0..mlen.*]); const writer = fbs.writer(); const bytes_written = m.write(writer) catch unreachable; - mlen.* = @truncate(bytes_written); + mlen.* = bytes_written; return CBEventType.NO_ERROR; } -fn successful_out(_: i32, _: [*]const u8, _: u32) CBEventType { +fn successful_out(_: i32, _: [*]const u8, _: usize) CBEventType { 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; } -fn unsuccessful_out(_: i32, _: [*]const u8, _: u32) CBEventType { +fn unsuccessful_out(_: i32, _: [*]const u8, _: usize) CBEventType { return CBEventType.ERROR; } @@ -311,7 +311,7 @@ test "nuke 'em" { 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. var stream: net.Stream = .{ .handle = origin }; 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); } - mlen.* = @truncate(packet_size); + mlen.* = packet_size; 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, // let's just recreate a Stream from the fd.