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