From d1c3e9a95ac9a1ae677c71e1dc68bb1f217ed0bd Mon Sep 17 00:00:00 2001 From: Karchnu Date: Mon, 13 Jul 2020 14:17:29 +0200 Subject: [PATCH] Bindings following libipc version 0.7.0. --- shard.yml | 4 +- src/ipc.cr | 9 +- src/ipc/client.cr | 48 ++------- src/ipc/connection.cr | 230 ------------------------------------------ src/ipc/context.cr | 152 ++++++++++++++++++++++++++++ src/ipc/event.cr | 81 +++++++++------ src/ipc/lowlevel.cr | 146 ++++++++++++++++++--------- src/ipc/message.cr | 19 ++-- src/ipc/server.cr | 28 +++++ src/ipc/service.cr | 99 ------------------ src/ipc/switch.cr | 1 - src/json.cr | 2 +- 12 files changed, 355 insertions(+), 464 deletions(-) delete mode 100644 src/ipc/connection.cr create mode 100644 src/ipc/context.cr create mode 100644 src/ipc/server.cr delete mode 100644 src/ipc/service.cr diff --git a/shard.yml b/shard.yml index 75230b9..cfb5da6 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: ipc -version: 0.6.0 +version: 0.7.0 authors: - Philippe Pittoli @@ -9,6 +9,6 @@ description: | High-level Crystal bindings to libipc. libraries: - libipc: ">= 0.5" + libipc: ">= 0.7" license: ISC diff --git a/src/ipc.cr b/src/ipc.cr index e242bd9..3da3c5a 100644 --- a/src/ipc.cr +++ b/src/ipc.cr @@ -1,11 +1,8 @@ -# TODO: more typing stuff. -# Functions return enum error not just int, for instance. - require "./ipc/lowlevel.cr" require "./ipc/exception.cr" require "./ipc/message.cr" -require "./ipc/connection.cr" require "./ipc/event.cr" -require "./ipc/client.cr" -require "./ipc/service.cr" require "./ipc/switch.cr" +require "./ipc/context.cr" +require "./ipc/client.cr" +require "./ipc/server.cr" diff --git a/src/ipc/client.cr b/src/ipc/client.cr index 124df1f..98448e3 100644 --- a/src/ipc/client.cr +++ b/src/ipc/client.cr @@ -1,43 +1,15 @@ -require "./lowlevel" -require "./message" -require "./event" -require "./service" -require "./connection" - -class IPC::Client < IPC::Connections - @connection : IPC::Connection - - def initialize(name : String) +class IPC::Client < IPC::Context + # By default, this is a client. + def initialize(service_name : String) super() - @connection = IPC::Connection.new name - self << @connection - end + r = LibIPC.ipc_connection(self.pointer, service_name) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "error during connection establishment: #{m}" + end - def initialize(name : String, &block : Proc(IPC::Event::Events|Exception, Nil)) - initialize name - ::loop &block - close - end - - def send(*args) - @connection.send *args - end - - def read(*args) - @connection.read *args - end - - # sanitizer - def fd - @connection.fd - end - - def loop(&block : Proc(IPC::Event::Events|Exception, Nil)) - super(nil, &block) - end - - def close - @connection.close + # Very important as there are filesystem side-effects. + at_exit { close } end end diff --git a/src/ipc/connection.cr b/src/ipc/connection.cr deleted file mode 100644 index 87e1ffb..0000000 --- a/src/ipc/connection.cr +++ /dev/null @@ -1,230 +0,0 @@ -require "./lowlevel" -require "./message" -require "./event" - -class IPC::Connection - getter connection : LibIPC::Connection - getter closed = false - - # connection already established - def initialize(c : LibIPC::Connection) - @connection = c - end - - def initialize(service_name : String) - @connection = LibIPC::Connection.new - - r = LibIPC.ipc_connection(LibC.environ, self.pointer, service_name) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "error during connection establishment: #{m}" - end - end - - def initialize(name, &block) - initialize(name) - - yield self - - close - end - - # Adds a new connection based on the socket file descriptor - def initialize(fd : Int32) - external_connection = LibIPC::Connection.new - external_connection.fd = fd - initialize(external_connection) - end - - # sanitizer - def fd - @connection.fd - end - - def send(utype : UInt8, payload : Bytes) - message = LibIPC::Message.new type: LibIPC::MessageType::Data.to_u8, - user_type: utype, - length: payload.bytesize, - payload: payload.to_unsafe - - r = LibIPC.ipc_write(self.pointer, pointerof(message)) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "error writing a message: #{m}" - end - end - - def send(utype : UInt8, payload : String) - send(utype, Bytes.new(payload.to_unsafe, payload.bytesize)) - end - - def send(message : IPC::Message) - send(message.utype, message.payload) - end - - def read - message = LibIPC::Message.new - r = LibIPC.ipc_read(pointerof(@connection), pointerof(message)) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "error reading a message: #{m}" - end - - IPC::Message.new pointerof(message) - end - - def close - return if @closed - - r = LibIPC.ipc_close(self.pointer) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot correctly close the connection: #{m}" - end - - @closed = true - end - - def pointer - pointerof(@connection) - end - - def type - @connection.type - end -end - -# This class is designed for stand alone connections, where the StandAloneConnection object -# should NOT be garbage collected (which means the end of the communication) -class IPC::StandAloneConnection - # close the connection in case the object is garbage collected - def finalize - close - end -end - -class IPC::Connections - property base_timer : Float64 = 0.0 - property timer : Float64 = 0.0 - getter connections : LibIPC::Connections - - def initialize - @connections = LibIPC::Connections.new - end - - def initialize(@connections : LibIPC::Connections) - end - - def << (client : IPC::Connection) - r = LibIPC.ipc_add(self.pointer, client.pointer) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot add an arbitrary file descriptor: #{m}" - end - end - - def << (fd : Int) - r = LibIPC.ipc_add_fd(self.pointer, fd) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot add an arbitrary file descriptor: #{m}" - end - end - - def remove (client : IPC::Connection) - c = client.connection - r = LibIPC.ipc_del(self.pointer, pointerof(c)) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot remove a client: #{m}" - end - end - - def remove_fd (fd : Int) - r = LibIPC.ipc_del_fd(self.pointer, fd) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot remove an arbitrary file descriptor: #{m}" - end - end - - def wait_event(server : IPC::Connection | Nil, &block) : Tuple(LibIPC::EventType, IPC::Message, IPC::Connection) | Tuple(LibIPC::EventType, Nil, Nil) - event = LibIPC::Event.new - - serverp = nil - unless server.nil? - serverp = server.pointer - end - - r = LibIPC.ipc_wait_event self.pointer, serverp, pointerof(event), pointerof(@timer) - if r.error_code != 0 - m = String.new r.error_message.to_slice - yield IPC::Exception.new "error waiting for a new event: #{m}" - end - - eventtype = event.type.unsafe_as(LibIPC::EventType) - - # if event type is Timer, there is no connection nor message - case eventtype - when LibIPC::EventType::Timer - return eventtype, nil, nil - end - - connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value - - message = event.message.unsafe_as(Pointer(LibIPC::Message)) - - return eventtype, IPC::Message.new(message), connection - end - - def loop(server : IPC::Connection | IPC::Server | ::Nil, &block : Proc(IPC::Event::Events|Exception, Nil)) - if @base_timer > 0 && @timer == 0 - @timer = @base_timer - end - - ::loop do - type, message, connection = wait_event server, &block - - case type - when LibIPC::EventType::Timer - # reset timer - @timer = @base_timer - yield IPC::Event::Timer.new - when LibIPC::EventType::Connection - yield IPC::Event::Connection.new connection.not_nil! - - when LibIPC::EventType::NotSet - yield IPC::Exception.new "even type not set" - - when LibIPC::EventType::Error - yield IPC::Exception.new "even type indicates an error" - - when LibIPC::EventType::ExtraSocket - yield IPC::Event::ExtraSocket.new message.not_nil!, connection.not_nil! - - when LibIPC::EventType::Switch - yield IPC::Event::Switch.new message.not_nil!, connection.not_nil! - - when LibIPC::EventType::Message - yield IPC::Event::Message.new message.not_nil!, connection.not_nil! - - # for now, the libipc does not provide lookup events - # networkd uses a simple LibIPC::EventType::Message - # when LibIPC::EventType::LookUp - # yield IPC::Event::LookUp.new message, connection - - when LibIPC::EventType::Disconnection - yield IPC::Event::Disconnection.new connection.not_nil! - end - end - end - - # sanitizer - def pointer - pointerof(@connections) - end - - def pp - LibIPC.ipc_connections_print @connections - end -end diff --git a/src/ipc/context.cr b/src/ipc/context.cr new file mode 100644 index 0000000..1b94a4a --- /dev/null +++ b/src/ipc/context.cr @@ -0,0 +1,152 @@ + +class IPC::Context + property base_timer : Int32 = LibIPC::INFTIM + property timer : Int32 = LibIPC::INFTIM + getter context : LibIPC::Ctx + + def initialize + @context = LibIPC::Ctx.new + end + + def initialize(@context : LibIPC::Ctx) + end + + def initialize(name : String, &block : Proc(IPC::Event::Events|Exception, Nil)) + initialize name + ::loop &block + close + end + + def << (fd : Int) + r = LibIPC.ipc_add_fd(self.pointer, fd) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "cannot add an arbitrary file descriptor: #{m}" + end + end + + def remove_index (index : UInt32) + r = LibIPC.ipc_del(self.pointer, index) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "cannot remove an arbitrary file descriptor: #{m}" + end + end + def remove_fd (fd : Int32) + r = LibIPC.ipc_del_fd(self.pointer, fd) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "cannot remove an arbitrary file descriptor: #{m}" + end + end + + def wait_event(&block) : IPC::Event::Events | Exception + event = LibIPC::Event.new + + r = LibIPC.ipc_wait_event self.pointer, pointerof(event), pointerof(@timer) + if r.error_code != 0 + m = String.new r.error_message.to_slice + yield IPC::Exception.new "error waiting for a new event: #{m}" + end + + eventtype = event.type.unsafe_as(LibIPC::EventType) + + # if event type is Timer, there is no connection nor message + case eventtype + when LibIPC::EventType::NotSet + return Exception.new "'Event type: not set" + when LibIPC::EventType::Error + return IPC::Event::Error.new event.origin, event.index + when LibIPC::EventType::ExtraSocket # Message received from a non IPC socket. + return IPC::Event::ExtraSocket.new event.origin, event.index + when LibIPC::EventType::Switch # Message to send to a corresponding fd. + return IPC::Event::Switch.new event.origin, event.index + when LibIPC::EventType::Connection # New user. + return IPC::Event::Connection.new event.origin, event.index + when LibIPC::EventType::Disconnection # User disconnected. + return IPC::Event::Disconnection.new event.origin, event.index + when LibIPC::EventType::Message # New message. + lowlevel_message = event.message.unsafe_as(Pointer(LibIPC::Message)) + ipc_message = IPC::Message.new lowlevel_message + return IPC::Event::MessageReceived.new event.origin, event.index, ipc_message + when LibIPC::EventType::LookUp # Client asking for a service through ipcd. + # for now, the libipc does not provide lookup events + # ipcd uses a simple LibIPC::EventType::Message + return IPC::Event::LookUp.new event.origin, event.index + when LibIPC::EventType::Timer # Timeout in the poll(2) function. + return IPC::Event::Timer.new + when LibIPC::EventType::Tx # Message sent. + return IPC::Event::MessageSent.new event.origin, event.index + end + + return Exception.new "Cannot understand the event type: #{eventtype}" + end + + def loop(&block : Proc(IPC::Event::Events|Exception, Nil)) + ::loop do + if @base_timer > 0 && @timer == 0 + @timer = @base_timer + end + + yield wait_event &block + end + end + + def send(fd : Int32, utype : UInt8, payload : Bytes) + message = LibIPC::Message.new fd: fd, + type: LibIPC::MessageType::Data.to_u8, + user_type: utype, + length: payload.bytesize, + payload: payload.to_unsafe + + r = LibIPC.ipc_write(self.pointer, pointerof(message)) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "error writing a message: #{m}" + end + end + + def send(fd : Int32, utype : UInt8, payload : String) + send(fd, utype, Bytes.new(payload.to_unsafe, payload.bytesize)) + end + + def send(message : IPC::Message) + send(message.fd, message.utype, message.payload) + end + + def read(index : UInt32) + message = LibIPC::Message.new + r = LibIPC.ipc_read(self.pointer, index, pointerof(message)) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "error reading a message: #{m}" + end + + IPC::Message.new pointerof(message) + end + + # sanitizer + def pointer + pointerof(@context) + end + + # sanitizer + def fd + @connection.fd + end + + def close + return if @closed + r = LibIPC.ipc_close_all(self.pointer) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "cannot correctly close the connection: #{m}" + end + LibIPC.ipc_ctx_free(self.pointer) + @closed = true + end + + def pp + LibIPC.ipc_ctx_print self.pointer + end +end diff --git a/src/ipc/event.cr b/src/ipc/event.cr index 4d655d0..502efb8 100644 --- a/src/ipc/event.cr +++ b/src/ipc/event.cr @@ -1,39 +1,54 @@ -require "./lowlevel" -require "./message" -require "./connection" class IPC::Event - alias Events = IPC::Event::Timer | IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Event::ExtraSocket | IPC::Event::Switch | IPC::Event::LookUp + alias Events = IPC::Event::Timer | + IPC::Event::Error | + IPC::Event::Connection | + IPC::Event::Disconnection | + IPC::Event::MessageReceived | + IPC::Event::ExtraSocket | + IPC::Event::Switch | + IPC::Event::LookUp | + IPC::Event::MessageSent +end - class Timer - end - - class Connection - getter connection : IPC::Connection - def initialize(@connection) - end - end - - class Disconnection - getter connection : IPC::Connection - def initialize(@connection) - end - end - - class Message - getter message : ::IPC::Message - getter connection : IPC::Connection - def initialize(@message, @connection) - end - end - - class ExtraSocket < IPC::Event::Message - end - - class Switch < IPC::Event::Message - end - - class LookUp < IPC::Event::Message +class IPC::Event::Timer < IPC::Event + def initialize end end +class IPC::Event::Base < IPC::Event + property fd : Int32 + property index : UInt32 + + def initialize(@fd, @index) + end +end + +class IPC::Event::Connection < IPC::Event::Base +end + +class IPC::Event::Disconnection < IPC::Event::Base +end + +class IPC::Event::Error < IPC::Event::Base +end + +class IPC::Event::MessageReceived < IPC::Event::Base + getter message : ::IPC::Message + + def initialize(@fd, @index, @message) + end +end + +class IPC::Event::ExtraSocket < IPC::Event::Base +end + +class IPC::Event::Switch < IPC::Event::Base +end + +class IPC::Event::LookUp < IPC::Event::Base +end + +class IPC::Event::MessageSent < IPC::Event::Base +end + diff --git a/src/ipc/lowlevel.cr b/src/ipc/lowlevel.cr index 6a6b0a7..e0b9b99 100644 --- a/src/ipc/lowlevel.cr +++ b/src/ipc/lowlevel.cr @@ -1,22 +1,33 @@ @[Link("ipc")] lib LibIPC - struct Connection - version : LibC::UInt - index : LibC::UInt - fd : LibC::Int - type : UInt8 - spath : LibC::Char* # [4096] # [PATH_MAX] + INFTIM = -1 + + enum ConnectionType + IPC # IO op. are handled by libipc. + External # IO op. are handled by the libipc user app. + Server # Should listen and accept new IPC users. + Switched # IO op. are handled by callbacks. end - struct Connections - cinfos : Connection** - size : LibC::Int + struct Connection + type : ConnectionType # + spath : LibC::Char* # [4096] # [PATH_MAX] + end + + struct Pollfd + fd : LibC::Int + events : LibC::Short + revents : LibC::Short end struct Switching origin : LibC::Int dest : LibC::Int + orig_cb_in : (Int32, Pointer(Message)) -> ConnectionType + orig_cb_out : (Int32, Pointer(Message)) -> ConnectionType + dest_cb_in : (Int32, Pointer(Message)) -> ConnectionType + dest_cb_out : (Int32, Pointer(Message)) -> ConnectionType end struct Switchings @@ -24,6 +35,14 @@ lib LibIPC size : LibC::UInt end + struct Ctx + cinfos : Connection* + pollfd : Pollfd* + size : LibC::UInt64T + tx : Messages + switchdb : Switchings + end + enum MessageType ServerClose Error @@ -31,29 +50,38 @@ lib LibIPC LookUp end + # Messages are stored in lists within the libipc before being sent. + struct Messages + messages : Message* + size : LibC::UInt64T + end + struct Message - type : UInt8 - user_type : UInt8 - length : LibC::UInt - payload : LibC::Char* + type : UInt8 # Internal message type. + user_type : UInt8 # User-defined message type. + fd : LibC::Int # fd of the sender. + length : LibC::UInt # Payload length. + payload : LibC::Char* # end enum EventType - NotSet - Error - ExtraSocket - Switch - Connection - Disconnection - Message - LookUp - Timer + NotSet # + Error # + ExtraSocket # Message received from a non IPC socket. + Switch # Message to send to a corresponding fd. + Connection # New user. + Disconnection # User disconnected. + Message # New message. + LookUp # Client asking for a service through ipcd. + Timer # Timeout in the poll(2) function. + Tx # Message sent. end struct Event - type : EventType - origin : Connection* - message : Message* + type : EventType # + index : LibC::UInt # Index of the sender in the ipc_ctx structure. + origin : LibC::Int # fd of the sender. + message : Message* # Pointer to the reveiced message. end struct IPCError @@ -62,46 +90,72 @@ lib LibIPC error_message : LibC::Char[8192] end - fun ipc_server_init(env : LibC::Char**, connection : Connection*, sname : LibC::Char*) : IPCError - fun ipc_server_close(Connection*) : IPCError - fun ipc_close(Connection*) : IPCError + # Connection functions. + # Context is allocated, ipcd is requested and the connection/initialisation is performed. + fun ipc_server_init(ctx : Ctx*, sname : LibC::Char*) : IPCError + fun ipc_connection(Ctx*, LibC::Char*) : IPCError + fun ipc_connection_switched(Ctx*, LibC::Char*, LibC::Int, Pointer(LibC::Int)) : IPCError - # connection to a service - fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : IPCError + # ipc_message_copy: pm, @fd, @mtype, @utype, @payload + fun ipc_message_copy(Message*, LibC::Int, UInt8, UInt8, LibC::Char*, Int32) - fun ipc_read(Connection*, Message*) : IPCError - fun ipc_write(Connection*, Message*) : IPCError + # Closing connections. + fun ipc_close(ctx : Ctx*, index : LibC::UInt64T) : IPCError + fun ipc_close_all(ctx : Ctx*) : IPCError - fun ipc_wait_event(Connections*, Connection*, Event*, LibC::Double*) : IPCError + fun ipc_ctx_free(Ctx*) # Void - fun ipc_add(Connections*, Connection*) : IPCError - fun ipc_del(Connections*, Connection*) : IPCError - fun ipc_add_fd(Connections*, LibC::Int) : IPCError - fun ipc_del_fd(Connections*, LibC::Int) : IPCError + # Loop function. + fun ipc_wait_event(Ctx*, Event*, LibC::Int*) : IPCError - fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt) : IPCError + # Adding and removing file discriptors to read. + fun ipc_add(Ctx*, Connection*, Pollfd*) : IPCError + fun ipc_del(Ctx*, LibC::UInt) : IPCError + fun ipc_add_fd(Ctx*, LibC::Int) : IPCError + fun ipc_add_fd_switched(Ctx*, LibC::Int) : IPCError + fun ipc_del_fd(Ctx*, LibC::Int) : IPCError - fun ipc_connections_free(Connections*) # Void - fun ipc_connections_close(Connections*) # Void + # Sending a message (will wait the fd to become available for IO operations). + fun ipc_write(Ctx*, Message*) : IPCError # This function let the user get the default error message based on the error code. # The error message is contained in the IPCError structure, this function should not be used, in most cases. fun ipc_errors_get (LibC::Int) : LibC::Char* - - # networkd-related functions - fun ipc_wait_event_networkd(Connections*, Connection*, Event*, Switchings*, LibC::Double*) : IPCError - + # Exchanging file descriptors (used with ipcd on connection). fun ipc_receive_fd (sock : LibC::Int, fd : LibC::Int*) : IPCError fun ipc_provide_fd (sock : LibC::Int, fd : LibC::Int ) : IPCError + # To change the type of a fd. + fun ipc_ctx_fd_type(Ctx*, LibC::Int, LibIPC::ConnectionType) : LibC::Int + + enum IPCCB + NoError # + Closing # + Error # + ParsingError # + Ignore # + end + + # Changing the callbacks for switched fd. + # ipc_switching_callbacks: ctx, fd + # , enum ipccb cb_in (fd, *ipc_message) + # , enum ipccb cb_out (fd, *ipc_message) + fun ipc_switching_callbacks(Ctx*, LibC::Int, + (LibC::Int, LibIPC::Message* -> LibIPC::IPCCB), + (LibC::Int, LibIPC::Message* -> LibIPC::IPCCB)) + + fun ipc_ctx_switching_add (ctx : Ctx*, fd1 : LibC::Int, fd2 : LibC::Int) # Void fun ipc_switching_add (switch : Switchings*, fd1 : LibC::Int, fd2 : LibC::Int) # Void fun ipc_switching_del (switch : Switchings*, fd : LibC::Int ) : LibC::Int fun ipc_switching_get (switch : Switchings*, fd : LibC::Int ) : LibC::Int fun ipc_switching_free (switch : Switchings* ) # Void - # non public functions (for testing purposes) + # non public functions + fun ipc_read(ctx : Ctx*, index : LibC::UInt, message : Message*) : IPCError + + # for testing purposes fun ipc_switching_print (switch : Switchings*) # Void fun service_path (path : LibC::Char*, sname : LibC::Char*, index : Int32, version : Int32) : IPCError - fun ipc_connections_print (Connections*) # Void + fun ipc_ctx_print (Ctx*) # Void end diff --git a/src/ipc/message.cr b/src/ipc/message.cr index 0276e9f..eff8eb4 100644 --- a/src/ipc/message.cr +++ b/src/ipc/message.cr @@ -1,11 +1,10 @@ -require "./lowlevel" require "json" # JSON is currently used for messages over websockets # At some point, this will be replaced by the CBOR format class IPC::Message - + property fd : Int32 # file descriptor property mtype : UInt8 # libipc message type property utype : UInt8 # libipc user message type property payload : Bytes @@ -24,7 +23,7 @@ class IPC::Message def self.from_json (str : String) jsonmessage = JSONMessage.from_json str - IPC::Message.new jsonmessage.mtype, jsonmessage.utype, jsonmessage.payload + IPC::Message.new 0, jsonmessage.mtype, jsonmessage.utype, jsonmessage.payload end def to_json @@ -34,10 +33,12 @@ class IPC::Message def initialize(message : Pointer(LibIPC::Message)) if message.null? @mtype = LibIPC::MessageType::Error.to_u8 + @fd = 0 @utype = 0 @payload = Bytes.new "".to_unsafe, 0 else m = message.value + @fd = m.fd @mtype = m.type @utype = m.user_type @payload = Bytes.new m.payload, m.length @@ -48,14 +49,12 @@ class IPC::Message initialize pointerof(message) end - def initialize(mtype, utype, payload : Bytes) + def initialize(@fd, mtype, @utype, @payload : Bytes) @mtype = mtype.to_u8 - @utype = utype - @payload = payload end - def initialize(mtype, utype, payload : String) - initialize(mtype, utype, Bytes.new(payload.to_unsafe, payload.bytesize)) + def initialize(fd, mtype, utype, payload : String) + initialize(fd, mtype, utype, Bytes.new(payload.to_unsafe, payload.bytesize)) end def self.to_packet (user_type : Int, message : String) @@ -76,6 +75,10 @@ class IPC::Message IPC::Message.to_packet @utype, String.new(@payload) end + def copy_to_message_pointer(pm : LibIPC::Message*) + LibIPC.ipc_message_copy pm, @fd, @mtype, @utype, @payload, @payload.size + end + def to_s "(internal) utype #{@mtype}, (user) utype #{@utype}, payload #{String.new @payload}" end diff --git a/src/ipc/server.cr b/src/ipc/server.cr new file mode 100644 index 0000000..fafd5cb --- /dev/null +++ b/src/ipc/server.cr @@ -0,0 +1,28 @@ + +# the server is a client with a different init function +# ipc_connection => ipc_server_init +class IPC::Server < IPC::Context + def initialize(name : String) + initialize() + r = LibIPC.ipc_server_init(self.pointer, name) + if r.error_code != 0 + m = String.new r.error_message.to_slice + raise Exception.new "cannot initialize the server named #{name}: #{m}" + end + + # Very important as there are filesystem side-effects. + at_exit { close } + end +end + +# TODO: replacing IPC::Service by the IPC::NetworkD class? +class IPC::SwitchingService < IPC::Server + property switch = IPC::Switch.new + + # automatic removal of the fd in the switching list + def remove_fd (fd : Int) + super + @switch.del fd + end +end + diff --git a/src/ipc/service.cr b/src/ipc/service.cr deleted file mode 100644 index c7094bd..0000000 --- a/src/ipc/service.cr +++ /dev/null @@ -1,99 +0,0 @@ -require "./lowlevel" -require "./message" -require "./event" -require "./connection" - -# the server is a connection with two different function calls -# ipc_connection => ipc_server_init -# ipc_close => ipc_server_close -class IPC::Server < IPC::Connection - def initialize(name : String) - @connection = LibIPC::Connection.new - r = LibIPC.ipc_server_init(LibC.environ, self.pointer, name) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot initialize the server named #{name}: #{m}" - end - - # Very important as there are filesystem side-effects. - at_exit { close } - end - - def close - return if @closed - - r = LibIPC.ipc_server_close(self.pointer) - if r.error_code != 0 - m = String.new r.error_message.to_slice - raise Exception.new "cannot close the server correctly: #{m}" - end - - @closed = true - end -end - -class IPC::Service < IPC::Connections - @service_info : IPC::Server - - def initialize(name : String) - @service_info = IPC::Server.new name - super() - end - - def initialize(name : String, &block : Proc(IPC::Event::Events|Exception, Nil)) - initialize name - loop &block - close - end - - # sanitizer - def fd - @service_info.fd - end - - def loop(&block : Proc(IPC::Event::Events|Exception, Nil)) - super(@service_info, &block) - end - - def close - @service_info.close - end -end - -# TODO: replacing IPC::Service by the IPC::NetworkD class? -class IPC::SwitchingService < IPC::Service - property switch = IPC::Switch.new - - # automatic removal of the fd in the switching list - def remove_fd (fd : Int) - super - @switch.del fd - end - - def wait_event(server : IPC::Connection , &block) : Tuple(LibIPC::EventType, IPC::Message, IPC::Connection) | Tuple(LibIPC::EventType, Nil, Nil) - event = LibIPC::Event.new - - serverp = server.pointer - r = LibIPC.ipc_wait_event_networkd self.pointer, serverp, pointerof(event), @switch.pointer, pointerof(@timer) - - if r.error_code != 0 - m = String.new r.error_message.to_slice - yield IPC::Exception.new "error waiting for a new event: #{m}" - end - - eventtype = event.type.unsafe_as(LibIPC::EventType) - - # if event type is Timer, there is no connection nor message - case eventtype - when LibIPC::EventType::Timer - return eventtype, nil, nil - end - - connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value - - message = event.message.unsafe_as(Pointer(LibIPC::Message)) - - return eventtype, IPC::Message.new(message), connection - end -end - diff --git a/src/ipc/switch.cr b/src/ipc/switch.cr index 273e826..b426877 100644 --- a/src/ipc/switch.cr +++ b/src/ipc/switch.cr @@ -1,4 +1,3 @@ -require "./lowlevel" class IPC::Switch @switch = LibIPC::Switchings.new diff --git a/src/json.cr b/src/json.cr index 034f95c..a4fbdaf 100644 --- a/src/json.cr +++ b/src/json.cr @@ -25,7 +25,7 @@ class IPC::JSON end end -class IPC::Connection +class IPC::Context def send(message : IPC::JSON) send message.type.to_u8, message.to_json end