From 966c49e1142012c07c1ce1ba541b140c40f5cd4d Mon Sep 17 00:00:00 2001
From: Philippe PITTOLI
Date: Mon, 3 Jun 2019 20:40:12 +0200
Subject: [PATCH] Bindings for the new implementation of libipc.
---
src/ipc.cr | 386 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 235 insertions(+), 151 deletions(-)
diff --git a/src/ipc.cr b/src/ipc.cr
index 1c1f5e0..c27e02e 100644
--- a/src/ipc.cr
+++ b/src/ipc.cr
@@ -1,17 +1,19 @@
+# TODO: more typing stuff.
+# Functions return enum error not just int, for instance.
@[Link("ipc")]
lib LibIPC
- struct Service
+ struct Connection
version : LibC::UInt
index : LibC::UInt
- spath : LibC::Char[4096] # [PATH_MAX]
fd : LibC::Int
+ type : UInt8
+ spath : LibC::Char* # [4096] # [PATH_MAX]
end
- struct Client
- version : LibC::UInt
- index : LibC::UInt
- fd : LibC::Int
+ struct Connections
+ cinfos : Connection**
+ size : LibC::Int
end
enum MessageType
@@ -20,21 +22,79 @@ lib LibIPC
Data
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
+
struct Message
type : UInt8
length : LibC::UInt
payload : LibC::Char*
end
- struct Clients
- clients : Client**
- size : LibC::Int
- end
-
enum EventType
NotSet
Error
- Stdin
+ ExtraSocket
Connection
Disconnection
Message
@@ -42,126 +102,41 @@ lib LibIPC
struct Event
type : EventType
- origin : Client*
- m : Message*
+ origin : Connection*
+ message : Message*
end
- # FIXME: IPC.initialize:
- # - throw exceptions on error.
- # - Make most arguments optional.
- fun ipc_server_init(env : LibC::Char**, service : Service*, sname : LibC::Char*) : LibC::Int
- # FIXME: IPC.(destroy?)
- fun ipc_server_close(Service*) : LibC::Int
- fun ipc_server_close_client(Client*) : LibC::Int
+ 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
- fun ipc_server_accept(Service*, Client*) : LibC::Int
- fun ipc_server_read(Client*, Message*) : LibC::Int
- fun ipc_server_write(Client*, Message*) : LibC::Int
+ # connection to a service
+ fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int
- fun ipc_server_select(Clients*, Service*, Clients*, LibC::Int*) : LibC::Int
+ fun ipc_accept(Connection*, Connection*) : LibC::Int
+ fun ipc_read(Connection*, Message*) : LibC::Int
+ fun ipc_write(Connection*, Message*) : LibC::Int
- fun ipc_service_poll_event(Clients*, Service*, Event*) : LibC::Int
+ fun ipc_wait_event(Connections*, Connection*, Event*) : LibC::Int
- fun ipc_application_connection(LibC::Char**, Service*, LibC::Char*) : LibC::Int
- fun ipc_application_close(Service*) : LibC::Int
- fun ipc_application_read(Service*, Message*) : LibC::Int
- fun ipc_application_write(Service*, Message*) : 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_client_add(Clients*, Client*) : LibC::Int
- fun ipc_client_del(Clients*, Client*) : LibC::Int
+ fun ipc_connection_copy(Connection*) : Connection*
+ fun ipc_connection_eq(Connection*, Connection*) : LibC::Int
- fun ipc_server_client_copy(Client*) : Client*
- fun ipc_server_client_eq(Client*, Client*) : LibC::Int
+ fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt)
- fun ipc_server_client_gen(Client*, LibC::UInt, LibC::UInt)
-
- fun ipc_clients_free(Clients*)
+ 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::Service
- @closed = false
- @clients = LibIPC::Clients.new
- # FIXME: getter only as long as proper bindings are unfinished
- getter service = LibIPC::Service.new
-
- def initialize(name : String)
- if LibIPC.ipc_server_init(LibC.environ, pointerof(@service), name) < 0
- raise Exception.new "ipc_server_init < 0" # FIXME: Add proper descriptions here.
- end
-
- # Very important as there are filesystem side-effects.
- at_exit { close }
- end
-
- def initialize(name : String, &block : Proc(IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message, Nil))
- initialize name
- loop &block
- close
- end
-
- def close
- return if @closed
-
- # FIXME: Probably check it’s not been closed already.
- if LibIPC.ipc_server_close(pointerof(@service)) < 0
- raise Exception.new "ipc_server_close < 0"
- end
-
- @closed = true
- end
- def finalize
- close
- end
-
- def accept
- ::IPC::Server::Client.new pointerof(@service)
- end
-
- def loop(&block)
- ::loop do
- event = LibIPC::Event.new
-
- r = LibIPC.ipc_service_poll_event pointerof(@clients), pointerof(@service), pointerof(event)
-
- if r < 0
- raise Exception.new "ipc_service_poll_event < 0"
- end
-
- client = IPC::RemoteClient.new event.origin.unsafe_as(Pointer(LibIPC::Client)).value
-
- pp! event
- message = event.m.unsafe_as(Pointer(LibIPC::Message))
- unless message.null?
- pp! message.value
- end
-
- case event.type
- when LibIPC::EventType::Connection
- yield IPC::Event::Connection.new client
- when LibIPC::EventType::Message
- message = event.m.unsafe_as(Pointer(LibIPC::Message)).value
-
- yield IPC::Event::Message.new IPC::Message.new(message.type, message.length, message.payload), client
- when LibIPC::EventType::Disconnection
- yield IPC::Event::Disconnection.new client
- end
- end
- end
end
class IPC::Message
- enum Type
- CLOSE
- CONNECTION
- SYN
- ACK
- DATA
- end
-
getter type : UInt8
getter payload : String
@@ -171,50 +146,48 @@ class IPC::Message
end
end
-class IPC::RemoteClient
- getter client : LibIPC::Client
-
- def initialize(@client)
- end
-
- def send(type : UInt8, payload : String)
- message = LibIPC::Message.new type: type, length: payload.bytesize, payload: payload.to_unsafe
-
- if LibIPC.ipc_server_write(pointerof(@client), pointerof(message)) < 0
- raise Exception.new "ipc_server_write < 0"
- end
- end
-end
-
class IPC::Event
class Connection
- getter client : IPC::RemoteClient
- def initialize(@client)
+ getter connection : IPC::Connection
+ def initialize(@connection)
end
end
class Disconnection
- getter client : IPC::RemoteClient
- def initialize(@client)
+ getter connection : IPC::Connection
+ def initialize(@connection)
end
end
class Message
getter message : ::IPC::Message
- getter client : IPC::RemoteClient
- def initialize(@message, @client)
+ getter connection : IPC::Connection
+ def initialize(@message, @connection)
end
end
+
+ class ExtraSocket < IPC::Event::Message
+ end
end
-class IPC::Client
- @service = LibIPC::Service.new
+class IPC::Connection
+ @closed = false
+ @connection : LibIPC::Connection
- def initialize(@service_name : String)
- if LibIPC.ipc_application_connection(LibC.environ, pointerof(@service), @service_name) < 0
- raise Exception.new "ipc_application_connection < 0"
+ # 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)
@@ -223,26 +196,137 @@ class IPC::Client
close
end
- def send(type, payload : String)
- message = LibIPC::Message.new type: type, length: payload.bytesize, payload: payload.to_unsafe
+ def send(type : LibIPC::MessageType, payload : String)
+ message = LibIPC::Message.new type: type.to_u8, length: payload.bytesize, payload: payload.to_unsafe
- if LibIPC.ipc_application_write(pointerof(@service), pointerof(message)) < 0
- raise Exception.new "ipc_application_write < 0"
+ 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 read
message = LibIPC::Message.new
- if LibIPC.ipc_application_read(pointerof(@service), pointerof(message)) < 0
- raise Exception.new "ipc_application_read < 0"
+ 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 message.type, message.length, message.payload
end
def close
- if LibIPC.ipc_application_close(pointerof(@service)) < 0
- raise Exception.new "ipc_application_close < 0"
+ return if @closed
+
+ r = LibIPC.ipc_close(pointerof(@connection))
+ if r != 0
+ m = String.new LibIPC.ipc_errors_get (r)
+ raise Exception.new "cannot close correctly the connection: #{m}"
+ end
+
+ @closed = true
+ end
+end
+
+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
+
+ alias Events = IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Event::ExtraSocket
+
+ def initialize(name : String, &block : Proc(Events, Nil))
+ initialize name
+ loop &block
+ close
+ end
+
+ def add_file_descriptor (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
+
+ # TODO: not implemented in libipc, yet.
+ # def del_file_descriptor (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 loop(&block)
+ ::loop do
+ 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
+
+ case event.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
+ message = event.message.unsafe_as(Pointer(LibIPC::Message)).value
+ yield IPC::Event::ExtraSocket.new IPC::Message.new(message.type, message.length, message.payload), connection
+
+ when LibIPC::EventType::Message
+ message = event.message.unsafe_as(Pointer(LibIPC::Message)).value
+ yield IPC::Event::Message.new IPC::Message.new(message.type, message.length, message.payload), connection
+
+ when LibIPC::EventType::Disconnection
+ yield IPC::Event::Disconnection.new connection
+ end
end
end
end