Bindings for the new implementation of libipc.

ipc07
Philippe PITTOLI 2019-06-03 20:40:12 +02:00
parent 2f6f58db5e
commit 966c49e114
1 changed files with 235 additions and 151 deletions

View File

@ -1,17 +1,19 @@
# TODO: more typing stuff.
# Functions return enum error not just int, for instance.
@[Link("ipc")] @[Link("ipc")]
lib LibIPC lib LibIPC
struct Service struct Connection
version : LibC::UInt version : LibC::UInt
index : LibC::UInt index : LibC::UInt
spath : LibC::Char[4096] # [PATH_MAX]
fd : LibC::Int fd : LibC::Int
type : UInt8
spath : LibC::Char* # [4096] # [PATH_MAX]
end end
struct Client struct Connections
version : LibC::UInt cinfos : Connection**
index : LibC::UInt size : LibC::Int
fd : LibC::Int
end end
enum MessageType enum MessageType
@ -20,21 +22,79 @@ lib LibIPC
Data Data
end 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 struct Message
type : UInt8 type : UInt8
length : LibC::UInt length : LibC::UInt
payload : LibC::Char* payload : LibC::Char*
end end
struct Clients
clients : Client**
size : LibC::Int
end
enum EventType enum EventType
NotSet NotSet
Error Error
Stdin ExtraSocket
Connection Connection
Disconnection Disconnection
Message Message
@ -42,126 +102,41 @@ lib LibIPC
struct Event struct Event
type : EventType type : EventType
origin : Client* origin : Connection*
m : Message* message : Message*
end end
# FIXME: IPC.initialize: fun ipc_server_init(env : LibC::Char**, connection : Connection*, sname : LibC::Char*) : LibC::Int
# - throw exceptions on error. fun ipc_server_close(Connection*) : LibC::Int
# - Make most arguments optional. fun ipc_close(Connection*) : LibC::Int
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_accept(Service*, Client*) : LibC::Int # connection to a service
fun ipc_server_read(Client*, Message*) : LibC::Int fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int
fun ipc_server_write(Client*, Message*) : 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_add(Connections*, Connection*) : LibC::Int
fun ipc_application_close(Service*) : LibC::Int fun ipc_del(Connections*, Connection*) : LibC::Int
fun ipc_application_read(Service*, Message*) : LibC::Int fun ipc_add_fd (Connections*, LibC::Int) : LibC::Int
fun ipc_application_write(Service*, Message*) : LibC::Int
fun ipc_client_add(Clients*, Client*) : LibC::Int fun ipc_connection_copy(Connection*) : Connection*
fun ipc_client_del(Clients*, Client*) : LibC::Int fun ipc_connection_eq(Connection*, Connection*) : LibC::Int
fun ipc_server_client_copy(Client*) : Client* fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt)
fun ipc_server_client_eq(Client*, Client*) : LibC::Int
fun ipc_server_client_gen(Client*, LibC::UInt, LibC::UInt) fun ipc_connections_free(Connections*)
fun ipc_get(Connections*)
fun ipc_clients_free(Clients*) fun ipc_errors_get (LibC::Int) : LibC::Char*
end end
class IPC::Exception < ::Exception 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 its 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 end
class IPC::Message class IPC::Message
enum Type
CLOSE
CONNECTION
SYN
ACK
DATA
end
getter type : UInt8 getter type : UInt8
getter payload : String getter payload : String
@ -171,50 +146,48 @@ class IPC::Message
end end
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 IPC::Event
class Connection class Connection
getter client : IPC::RemoteClient getter connection : IPC::Connection
def initialize(@client) def initialize(@connection)
end end
end end
class Disconnection class Disconnection
getter client : IPC::RemoteClient getter connection : IPC::Connection
def initialize(@client) def initialize(@connection)
end end
end end
class Message class Message
getter message : ::IPC::Message getter message : ::IPC::Message
getter client : IPC::RemoteClient getter connection : IPC::Connection
def initialize(@message, @client) def initialize(@message, @connection)
end end
end end
class ExtraSocket < IPC::Event::Message
end
end end
class IPC::Client class IPC::Connection
@service = LibIPC::Service.new @closed = false
@connection : LibIPC::Connection
def initialize(@service_name : String) # connection already established
if LibIPC.ipc_application_connection(LibC.environ, pointerof(@service), @service_name) < 0 def initialize(c : LibIPC::Connection)
raise Exception.new "ipc_application_connection < 0" @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
end end
def initialize(name, &block) def initialize(name, &block)
initialize(name) initialize(name)
@ -223,26 +196,137 @@ class IPC::Client
close close
end end
def send(type, payload : String) def send(type : LibIPC::MessageType, payload : String)
message = LibIPC::Message.new type: type, length: payload.bytesize, payload: payload.to_unsafe 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 r = LibIPC.ipc_write(pointerof(@connection), pointerof(message))
raise Exception.new "ipc_application_write < 0" if r != 0
m = String.new LibIPC.ipc_errors_get (r)
raise Exception.new "error writing a message: #{m}"
end end
end end
def read def read
message = LibIPC::Message.new message = LibIPC::Message.new
if LibIPC.ipc_application_read(pointerof(@service), pointerof(message)) < 0 r = LibIPC.ipc_read(pointerof(@connection), pointerof(message))
raise Exception.new "ipc_application_read < 0" if r != 0
m = String.new LibIPC.ipc_errors_get (r)
raise Exception.new "error reading a message: #{m}"
end end
IPC::Message.new message.type, message.length, message.payload IPC::Message.new message.type, message.length, message.payload
end end
def close def close
if LibIPC.ipc_application_close(pointerof(@service)) < 0 return if @closed
raise Exception.new "ipc_application_close < 0"
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 end
end end