From 0330f384ef07049a29c82e72de24ab95bf444111 Mon Sep 17 00:00:00 2001 From: Philippe PITTOLI Date: Sat, 27 Jul 2019 15:29:27 +0200 Subject: [PATCH] v0.2 - new API --- shard.yml | 7 +- src/ipc.cr | 376 +----------------------------------------- src/ipc/client.cr | 43 +++++ src/ipc/connection.cr | 202 +++++++++++++++++++++++ src/ipc/event.cr | 36 ++++ src/ipc/exception.cr | 3 + src/ipc/lowlevel.cr | 167 +++++++++++++++++++ src/ipc/message.cr | 40 +++++ src/ipc/service.cr | 92 +++++++++++ src/ipc/switch.cr | 33 ++++ 10 files changed, 628 insertions(+), 371 deletions(-) create mode 100644 src/ipc/client.cr create mode 100644 src/ipc/connection.cr create mode 100644 src/ipc/event.cr create mode 100644 src/ipc/exception.cr create mode 100644 src/ipc/lowlevel.cr create mode 100644 src/ipc/message.cr create mode 100644 src/ipc/service.cr create mode 100644 src/ipc/switch.cr diff --git a/shard.yml b/shard.yml index d226c66..3921b7e 100644 --- a/shard.yml +++ b/shard.yml @@ -1,13 +1,14 @@ name: ipc -version: 0.1.0 +version: 0.2.0 authors: + - Philippe Pittoli - Luka Vandervelden description: | - High-level Crystal bindings to libipc. + High-level Crystal bindings to libipc. libraries: libipc: ">= 0.1" -license: MIT +license: ISC diff --git a/src/ipc.cr b/src/ipc.cr index 6f94d17..e242bd9 100644 --- a/src/ipc.cr +++ b/src/ipc.cr @@ -1,371 +1,11 @@ # TODO: more typing stuff. # Functions return enum error not just int, for instance. -@[Link("ipc")] -lib LibIPC - struct Connection - version : LibC::UInt - index : LibC::UInt - fd : LibC::Int - type : UInt8 - spath : LibC::Char* # [4096] # [PATH_MAX] - end - - struct Connections - cinfos : Connection** - size : LibC::Int - end - - enum Errors - None = 0 - NotEnoughMemory - ClosedRecipient - ServerInitNoEnvironmentParam - ServerInitNoServiceParam - ServerInitNoServerNameParam - ServerInitMalloc - ConnectionNoServer - ConnectionNoServiceName - ConnectionNoEnvironmentParam - ConnectionGenNoCinfo - AcceptNoServiceParam - AcceptNoClientParam - Accept - HandleNewConnectionNoCinfoParam - HandleNewConnectionNoCinfosParam - WaitEventSelect - WaitEventNoClientsParam - WaitEventNoEventParam - HandleNewConnectionMalloc - AddEmptyList - AddNoParamClients - AddNoParamClient - AddFdNoParamCinfos - DelEmptyList - DelEmptiedList - DelCannotFindClient - DelNoClientsParam - DelNoClientParam - UsockSend - UsockConnectSocket - UsockConnectWrongFileDescriptor - UsockConnectEmptyPath - UsockClose - UsockRemoveUnlink - UsockRemoveNoFile - UsockInitEmptyFileDescriptor - UsockInitWrongFileDescriptor - UsockInitEmptyPath - UsockInitBind - UsockInitListen - UsockAcceptPathFileDescriptor - UsockAccept - UsockRecvNoBuffer - UsockRecvNoLength - UsockRecv - MessageNewNoMessageParam - MessageReadNomessageparam - MessageWriteNoMessageParam - MessageWriteNotEnoughData - MessageFormatNoMessageParam - MessageFormatInconsistentParams - MessageFormatLength - MessageFormatWriteEmptyMessage - MessageFormatWriteEmptyMsize - MessageFormatWriteEmptyBuffer - MessageFormatReadEmptyMessage - MessageFormatReadEmptyBuffer - MessageFormatReadMessageSize - MessageEmptyEmptyMessageList - end - - enum MessageType - ServerClose - Error - Data - end - - struct Message - type : UInt8 - user_type : UInt8 - length : LibC::UInt - payload : LibC::Char* - end - - enum EventType - NotSet - Error - ExtraSocket - Connection - Disconnection - Message - end - - struct Event - type : EventType - origin : Connection* - message : Message* - end - - fun ipc_server_init(env : LibC::Char**, connection : Connection*, sname : LibC::Char*) : LibC::Int - fun ipc_server_close(Connection*) : LibC::Int - fun ipc_close(Connection*) : LibC::Int - - # connection to a service - fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int - - fun ipc_read(Connection*, Message*) : LibC::Int - fun ipc_write(Connection*, Message*) : LibC::Int - - fun ipc_wait_event(Connections*, Connection*, Event*) : LibC::Int - - fun ipc_add(Connections*, Connection*) : LibC::Int - fun ipc_del(Connections*, Connection*) : LibC::Int - fun ipc_add_fd (Connections*, LibC::Int) : LibC::Int - - fun ipc_connection_copy(Connection*) : Connection* - fun ipc_connection_eq(Connection*, Connection*) : LibC::Int - - fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt) - - fun ipc_connections_free(Connections*) - fun ipc_get(Connections*) - fun ipc_errors_get (LibC::Int) : LibC::Char* -end - -class IPC::Exception < ::Exception -end - -class IPC::Message - getter mtype : UInt8 # libipc message type - getter type : UInt8 # libipc user message type - getter payload : Bytes - - def initialize(message : Pointer(LibIPC::Message)) - if message.null? - @mtype = LibIPC::MessageType::Error.to_u8 - @type = 0 - @payload = Bytes.new "".to_unsafe, 0 - else - m = message.value - @mtype = m.type - @type = m.user_type - @payload = Bytes.new m.payload, m.length - end - end - - def initialize(mtype, type, payload : Bytes) - @mtype = mtype.to_u8 - @type = type - @payload = payload - end - - def initialize(mtype, type, payload : String) - initialize(mtype, type, Bytes.new(payload.to_unsafe, payload.bytesize)) - end -end - -class IPC::Event - 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 -end - -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, pointerof(@connection), service_name) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "error during connection establishment: #{m}" - end - end - - def initialize(name, &block) - initialize(name) - - yield self - - close - end - - def send(type : UInt8, payload : Bytes) - message = LibIPC::Message.new type: LibIPC::MessageType::Data.to_u8, user_type: type, length: payload.bytesize, payload: payload.to_unsafe - - r = LibIPC.ipc_write(pointerof(@connection), pointerof(message)) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "error writing a message: #{m}" - end - end - - def send(type : UInt8, payload : String) - send(type, Bytes.new(payload.to_unsafe, payload.bytesize)) - end - - def send(message : IPC::Message) - send(message.type, message.payload) - end - - def read - message = LibIPC::Message.new - r = LibIPC.ipc_read(pointerof(@connection), pointerof(message)) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "error reading a message: #{m}" - end - - IPC::Message.new pointerof(message) - end - - def close - return if @closed - - r = LibIPC.ipc_close(pointerof(@connection)) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "cannot correctly close the connection: #{m}" - end - - @closed = true - end -end - -alias Events = IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Event::ExtraSocket - -class IPC::Service - @closed = false - @connections = LibIPC::Connections.new - @service_info = LibIPC::Connection.new - - def initialize(name : String) - r = LibIPC.ipc_server_init(LibC.environ, pointerof(@service_info), name) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "cannot initialize the server named #{name}: #{m}" - end - - # Very important as there are filesystem side-effects. - at_exit { close } - end - - def initialize(name : String, &block : Proc(Events|Exception, Nil)) - initialize name - loop &block - close - end - - def << (connection : IPC::Connection) - c = connection.connection - r = LibIPC.ipc_add(pointerof(@connections), pointerof(c)) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "cannot add an arbitrary file descriptor: #{m}" - end - end - - def << (fd : Int) - r = LibIPC.ipc_add_fd(pointerof(@connections), fd) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "cannot add an arbitrary file descriptor: #{m}" - end - end - - def remove_fd (fd : Int) - r = LibIPC.ipc_del_fd(pointerof(@connections), fd) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "cannot remove an arbitrary file descriptor: #{m}" - end - end - - def close - return if @closed - - r = LibIPC.ipc_server_close(pointerof(@service_info)) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - raise Exception.new "cannot close the server correctly: #{m}" - end - - @closed = true - end - - def finalize - close - end - - def wait_event(&block) : Tuple(LibIPC::EventType, IPC::Message, IPC::Connection) - event = LibIPC::Event.new - - r = LibIPC.ipc_wait_event pointerof(@connections), pointerof(@service_info), pointerof(event) - if r != 0 - m = String.new LibIPC.ipc_errors_get (r) - yield IPC::Exception.new "error waiting for a new event: #{m}" - end - - connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value - - pp! event - message = event.message.unsafe_as(Pointer(LibIPC::Message)) - unless message.null? - pp! message.value - end - - return event.type, IPC::Message.new(message), connection - end - - def loop(&block : Proc(Events|Exception, Nil)) - ::loop do - type, message, connection = wait_event &block - - case type - when LibIPC::EventType::Connection - yield IPC::Event::Connection.new connection - - 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, connection - - when LibIPC::EventType::Message - yield IPC::Event::Message.new message, connection - - when LibIPC::EventType::Disconnection - yield IPC::Event::Disconnection.new connection - end - end - end -end - +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" diff --git a/src/ipc/client.cr b/src/ipc/client.cr new file mode 100644 index 0000000..d8d7caf --- /dev/null +++ b/src/ipc/client.cr @@ -0,0 +1,43 @@ +require "./lowlevel" +require "./message" +require "./event" +require "./service" +require "./connection" + + +class IPC::Client < IPC::Connections + @connection : IPC::Connection + + def initialize(name : String) + super() + @connection = IPC::Connection.new name + self << @connection + end + + def initialize(name : String, &block : Proc(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(Events|Exception, Nil)) + super(nil, &block) + end + + def close + @connection.close + end +end diff --git a/src/ipc/connection.cr b/src/ipc/connection.cr new file mode 100644 index 0000000..8492779 --- /dev/null +++ b/src/ipc/connection.cr @@ -0,0 +1,202 @@ +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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + raise Exception.new "error during connection establishment: #{m}" + end + end + + def initialize(name, &block) + initialize(name) + + yield self + + close + end + + # sanitizer + def fd + @connection.fd + end + + def send(type : UInt8, payload : Bytes) + message = LibIPC::Message.new type: LibIPC::MessageType::Data.to_u8, user_type: type, length: payload.bytesize, payload: payload.to_unsafe + + r = LibIPC.ipc_write(self.pointer, pointerof(message)) + if r != 0 + m = String.new LibIPC.ipc_errors_get (r) + raise Exception.new "error writing a message: #{m}" + end + end + + def send(type : UInt8, payload : String) + send(type, Bytes.new(payload.to_unsafe, payload.bytesize)) + end + + def send(message : IPC::Message) + send(message.type, message.payload) + end + + def read + message = LibIPC::Message.new + r = LibIPC.ipc_read(pointerof(@connection), pointerof(message)) + if r != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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 + 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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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) + event = LibIPC::Event.new + + serverp = nil + unless server.nil? + serverp = server.pointer + end + + r = LibIPC.ipc_wait_event self.pointer, serverp, pointerof(event) + if r != 0 + m = String.new LibIPC.ipc_errors_get (r) + yield IPC::Exception.new "error waiting for a new event: #{m}" + end + + connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value + + eventtype = event.type.unsafe_as(LibIPC::EventType) + 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(Events|Exception, Nil)) + ::loop do + type, message, connection = wait_event server, &block + + case type + when LibIPC::EventType::Connection + yield IPC::Event::Connection.new connection + + 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, connection + + when LibIPC::EventType::Switch + yield IPC::Event::Switch.new message, connection + + when LibIPC::EventType::Message + yield IPC::Event::Message.new message, connection + + # 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 + end + end + end + + # sanitizer + def pointer + pointerof(@connections) + end + + def pp + LibIPC.ipc_connections_print @connections + end +end diff --git a/src/ipc/event.cr b/src/ipc/event.cr new file mode 100644 index 0000000..f048e93 --- /dev/null +++ b/src/ipc/event.cr @@ -0,0 +1,36 @@ +require "./lowlevel" +require "./message" +require "./connection" + +class IPC::Event + 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 + end +end + +alias Events = IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Event::ExtraSocket | IPC::Event::Switch | IPC::Event::LookUp + diff --git a/src/ipc/exception.cr b/src/ipc/exception.cr new file mode 100644 index 0000000..7f84111 --- /dev/null +++ b/src/ipc/exception.cr @@ -0,0 +1,3 @@ + +class IPC::Exception < ::Exception +end diff --git a/src/ipc/lowlevel.cr b/src/ipc/lowlevel.cr new file mode 100644 index 0000000..ba9b0b4 --- /dev/null +++ b/src/ipc/lowlevel.cr @@ -0,0 +1,167 @@ + +@[Link("ipc")] +lib LibIPC + struct Connection + version : LibC::UInt + index : LibC::UInt + fd : LibC::Int + type : UInt8 + spath : LibC::Char* # [4096] # [PATH_MAX] + end + + struct Connections + cinfos : Connection** + size : LibC::Int + end + + struct Switching + origin : LibC::Int + dest : LibC::Int + end + + struct Switchings + collection : Switching* + size : LibC::UInt + end + + enum Errors + None = 0 + NotEnoughMemory + ClosedRecipient + ServerInitNoEnvironmentParam + ServerInitNoServiceParam + ServerInitNoServerNameParam + ServerInitMalloc + ConnectionNoServer + ConnectionNoServiceName + ConnectionNoEnvironmentParam + ConnectionGenNoCinfo + AcceptNoServiceParam + AcceptNoClientParam + Accept + HandleNewConnectionNoCinfoParam + HandleNewConnectionNoCinfosParam + WaitEventSelect + WaitEventNoClientsParam + WaitEventNoEventParam + HandleNewConnectionMalloc + AddEmptyList + AddNoParamClients + AddNoParamClient + AddFdNoParamCinfos + DelEmptyList + DelEmptiedList + DelCannotFindClient + DelNoClientsParam + DelNoClientParam + UsockSend + UsockConnectSocket + UsockConnectWrongFileDescriptor + UsockConnectEmptyPath + UsockConnectConnect + UsockClose + UsockRemoveUnlink + UsockRemoveNoFile + UsockInitEmptyFileDescriptor + UsockInitWrongFileDescriptor + UsockInitEmptyPath + UsockInitBind + UsockInitListen + UsockAcceptPathFileDescriptor + UsockAccept + UsockRecvNoBuffer + UsockRecvNoLength + UsockRecv + MessageNewNoMessageParam + MessageReadNomessageparam + MessageWriteNoMessageParam + MessageWriteNotEnoughData + MessageFormatNoMessageParam + MessageFormatInconsistentParams + MessageFormatLength + MessageFormatWriteEmptyMessage + MessageFormatWriteEmptyMsize + MessageFormatWriteEmptyBuffer + MessageFormatReadEmptyMessage + MessageFormatReadEmptyBuffer + MessageFormatReadMessageSize + MessageEmptyEmptyMessageList + end + + enum MessageType + ServerClose + Error + Data + LookUp + end + + struct Message + type : UInt8 + user_type : UInt8 + length : LibC::UInt + payload : LibC::Char* + end + + enum EventType + NotSet + Error + ExtraSocket + Switch + Connection + Disconnection + Message + LookUp + end + + struct Event + type : EventType + origin : Connection* + message : Message* + end + + fun ipc_server_init(env : LibC::Char**, connection : Connection*, sname : LibC::Char*) : LibC::Int + fun ipc_server_close(Connection*) : LibC::Int + fun ipc_close(Connection*) : LibC::Int + + # connection to a service + fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int + + fun ipc_read(Connection*, Message*) : LibC::Int + fun ipc_write(Connection*, Message*) : LibC::Int + + fun ipc_wait_event(Connections*, Connection*, Event*) : LibC::Int + + fun ipc_add(Connections*, Connection*) : LibC::Int + fun ipc_del(Connections*, Connection*) : LibC::Int + fun ipc_add_fd(Connections*, LibC::Int) : LibC::Int + fun ipc_del_fd(Connections*, LibC::Int) : LibC::Int + + fun ipc_connection_copy(Connection*) : Connection* + fun ipc_connection_eq(Connection*, Connection*) : LibC::Int + + fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt) + + fun ipc_connections_free(Connections*) + fun ipc_connections_close(Connections*) + fun ipc_get(Connections*) + fun ipc_errors_get (LibC::Int) : LibC::Char* + + + # networkd-related functions + fun ipc_wait_event_networkd(Connections*, Connection*, Event*, Switchings*) : LibC::Int + + fun ipc_receive_fd (sock : LibC::Int, fd : LibC::Int*) : LibC::Int + fun ipc_provide_fd (sock : LibC::Int, fd : LibC::Int) : LibC::Int + + fun ipc_switching_add (switch : Switchings*, fd1 : LibC::Int, fd2 : LibC::Int) + 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* ) : LibC::Int + fun ipc_switching_print (switch : Switchings*) + + # non public functions (for testing purposes) + fun service_path (path : LibC::Char*, sname : LibC::Char*, index : Int32, version : Int32) : LibC::Int + fun log_get_logfile_dir (buf : LibC::Char*, size : LibC::UInt) : LibC::Char* + fun log_get_logfile_name (buf : LibC::Char*, size : LibC::UInt) + fun ipc_connections_print(Connections*) +end diff --git a/src/ipc/message.cr b/src/ipc/message.cr new file mode 100644 index 0000000..b5c4cae --- /dev/null +++ b/src/ipc/message.cr @@ -0,0 +1,40 @@ +require "./lowlevel" + +class IPC::Message + getter mtype : UInt8 # libipc message type + property type : UInt8 # libipc user message type + property payload : Bytes + + def initialize(message : Pointer(LibIPC::Message)) + if message.null? + @mtype = LibIPC::MessageType::Error.to_u8 + @type = 0 + @payload = Bytes.new "".to_unsafe, 0 + else + m = message.value + @mtype = m.type + @type = m.user_type + @payload = Bytes.new m.payload, m.length + end + end + + def initialize(message : LibIPC::Message) + initialize pointerof(message) + end + + def initialize(mtype, type, payload : Bytes) + @mtype = mtype.to_u8 + @type = type + @payload = payload + end + + def initialize(mtype, type, payload : String) + initialize(mtype, type, Bytes.new(payload.to_unsafe, payload.bytesize)) + end + + def to_s + "(internal) type #{@mtype}, (user) type #{@type}, payload #{String.new @payload}" + end + +end + diff --git a/src/ipc/service.cr b/src/ipc/service.cr new file mode 100644 index 0000000..68f07ee --- /dev/null +++ b/src/ipc/service.cr @@ -0,0 +1,92 @@ +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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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 != 0 + m = String.new LibIPC.ipc_errors_get (r) + 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(Events|Exception, Nil)) + initialize name + loop &block + close + end + + # sanitizer + def fd + @service_info.fd + end + + def loop(&block : Proc(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) + event = LibIPC::Event.new + + serverp = server.pointer + r = LibIPC.ipc_wait_event_networkd self.pointer, serverp, pointerof(event), @switch.pointer + if r != 0 + m = String.new LibIPC.ipc_errors_get (r) + yield IPC::Exception.new "error waiting for a new event: #{m}" + end + + connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value + + message = event.message.unsafe_as(Pointer(LibIPC::Message)) + + eventtype = event.type.unsafe_as(LibIPC::EventType) + + return eventtype, IPC::Message.new(message), connection + end +end + diff --git a/src/ipc/switch.cr b/src/ipc/switch.cr new file mode 100644 index 0000000..273e826 --- /dev/null +++ b/src/ipc/switch.cr @@ -0,0 +1,33 @@ +require "./lowlevel" + +class IPC::Switch + @switch = LibIPC::Switchings.new + + def inilialize + end + + def add (fd1 : Int32, fd2 : Int32) + LibIPC.ipc_switching_add self.pointer, fd1, fd2 + end + + def del (fd : Int32) + LibIPC.ipc_switching_del self.pointer, fd + end + + def close + LibIPC.ipc_switching_free self.pointer + end + + def print + LibIPC.ipc_switching_print self.pointer + end + + def finalize + close + end + + # sanitizer + def pointer + pointerof(@switch) + end +end