2020-07-03 13:42:19 +02:00
|
|
|
require "./lowlevel"
|
|
|
|
require "./message"
|
|
|
|
require "./event"
|
2020-07-03 13:48:39 +02:00
|
|
|
require "./connection"
|
2020-07-03 13:42:19 +02:00
|
|
|
|
|
|
|
class IPC::Context
|
|
|
|
property base_timer : Float64 = 0.0
|
|
|
|
property timer : Float64 = 0.0
|
|
|
|
getter context : LibIPC::Ctx
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@context = LibIPC::Ctx.new
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(@context : LibIPC::Ctx)
|
|
|
|
end
|
|
|
|
|
|
|
|
# By default, this is a client.
|
|
|
|
def initialize(service_name : String, &block)
|
|
|
|
initialize(name)
|
|
|
|
yield self
|
|
|
|
close
|
|
|
|
end
|
|
|
|
|
|
|
|
def << (client : IPC::Connection)
|
|
|
|
r = LibIPC.ipc_add(self.pointer, client.pointer, pointerof(client.pollfd))
|
|
|
|
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(&block) : IPC::Event::Events | Exception
|
|
|
|
event = LibIPC::Event.new
|
|
|
|
|
|
|
|
r = LibIPC.ipc_events_loop 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.index, event.origin
|
|
|
|
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.
|
|
|
|
message = event.message.unsafe_as(Pointer(LibIPC::Message))
|
|
|
|
return IPC::Event::MessageReceived.new event.origin, event.index, 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::Tx.new event.origin, event.index
|
|
|
|
end
|
|
|
|
|
|
|
|
yield Exception.new "Cannot understand the event type: #{eventtype}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def loop(&block : Proc(IPC::Event::Events|Exception, Nil))
|
|
|
|
if @base_timer > 0 && @timer == 0
|
|
|
|
@timer = @base_timer
|
|
|
|
end
|
|
|
|
|
|
|
|
::loop do
|
|
|
|
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(fd : Int32, message : IPC::Message)
|
|
|
|
send(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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
@closed = true
|
|
|
|
end
|
|
|
|
|
|
|
|
# def pp
|
|
|
|
# LibIPC.ipc_connections_print @context
|
|
|
|
# end
|
|
|
|
end
|