231 lines
5.7 KiB
Crystal
231 lines
5.7 KiB
Crystal
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.error_code != 0
|
|
m = String.new r.error_message.to_slice
|
|
raise Exception.new "error during connection establishment: #{m}"
|
|
end
|
|
end
|
|
|
|
def initialize(name, &block)
|
|
initialize(name)
|
|
|
|
yield self
|
|
|
|
close
|
|
end
|
|
|
|
# Adds a new connection based on the socket file descriptor
|
|
def initialize(fd : Int32)
|
|
external_connection = LibIPC::Connection.new
|
|
external_connection.fd = fd
|
|
initialize(external_connection)
|
|
end
|
|
|
|
# sanitizer
|
|
def fd
|
|
@connection.fd
|
|
end
|
|
|
|
def send(utype : UInt8, payload : Bytes)
|
|
message = LibIPC::Message.new 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(utype : UInt8, payload : String)
|
|
send(utype, Bytes.new(payload.to_unsafe, payload.bytesize))
|
|
end
|
|
|
|
def send(message : IPC::Message)
|
|
send(message.utype, message.payload)
|
|
end
|
|
|
|
def read
|
|
message = LibIPC::Message.new
|
|
r = LibIPC.ipc_read(pointerof(@connection), 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
|
|
|
|
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
|
|
property base_timer : Float64 = 0.0
|
|
property timer : Float64 = 0.0
|
|
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.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(server : IPC::Connection | Nil, &block) : Tuple(LibIPC::EventType, IPC::Message, IPC::Connection) | Tuple(LibIPC::EventType, Nil, Nil)
|
|
event = LibIPC::Event.new
|
|
|
|
serverp = nil
|
|
unless server.nil?
|
|
serverp = server.pointer
|
|
end
|
|
|
|
r = LibIPC.ipc_wait_event self.pointer, serverp, 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::Timer
|
|
return eventtype, nil, nil
|
|
end
|
|
|
|
connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value
|
|
|
|
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(IPC::Event::Events|Exception, Nil))
|
|
if @base_timer > 0 && @timer == 0
|
|
@timer = @base_timer
|
|
end
|
|
|
|
::loop do
|
|
type, message, connection = wait_event server, &block
|
|
|
|
case type
|
|
when LibIPC::EventType::Timer
|
|
# reset timer
|
|
@timer = @base_timer
|
|
yield IPC::Event::Timer.new
|
|
when LibIPC::EventType::Connection
|
|
yield IPC::Event::Connection.new connection.not_nil!
|
|
|
|
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.not_nil!, connection.not_nil!
|
|
|
|
when LibIPC::EventType::Switch
|
|
yield IPC::Event::Switch.new message.not_nil!, connection.not_nil!
|
|
|
|
when LibIPC::EventType::Message
|
|
yield IPC::Event::Message.new message.not_nil!, connection.not_nil!
|
|
|
|
# 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.not_nil!
|
|
end
|
|
end
|
|
end
|
|
|
|
# sanitizer
|
|
def pointer
|
|
pointerof(@connections)
|
|
end
|
|
|
|
def pp
|
|
LibIPC.ipc_connections_print @connections
|
|
end
|
|
end
|