ipc.cr-old/src/ipc.cr

372 lines
8.6 KiB
Crystal
Raw Normal View History

# TODO: more typing stuff.
# Functions return enum error not just int, for instance.
2018-11-20 11:06:15 +01:00
@[Link("ipc")]
lib LibIPC
struct Connection
2018-11-20 11:06:15 +01:00
version : LibC::UInt
index : LibC::UInt
fd : LibC::Int
type : UInt8
spath : LibC::Char* # [4096] # [PATH_MAX]
2018-11-20 11:06:15 +01:00
end
struct Connections
cinfos : Connection**
size : LibC::Int
2018-11-20 11:06:15 +01:00
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
2018-11-20 11:06:15 +01:00
struct Message
2019-06-04 01:36:21 +02:00
type : UInt8
user_type : UInt8
length : LibC::UInt
payload : LibC::Char*
2018-11-20 11:06:15 +01:00
end
enum EventType
NotSet
Error
ExtraSocket
2018-11-20 11:06:15 +01:00
Connection
Disconnection
Message
end
struct Event
type : EventType
origin : Connection*
message : Message*
2018-11-20 11:06:15 +01:00
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
2018-11-20 11:06:15 +01:00
# connection to a service
fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int
2018-11-20 11:06:15 +01:00
fun ipc_read(Connection*, Message*) : LibC::Int
fun ipc_write(Connection*, Message*) : LibC::Int
2018-11-20 11:06:15 +01:00
fun ipc_wait_event(Connections*, Connection*, Event*) : LibC::Int
2018-11-20 11:06:15 +01:00
fun ipc_add(Connections*, Connection*) : LibC::Int
fun ipc_del(Connections*, Connection*) : LibC::Int
fun ipc_add_fd (Connections*, LibC::Int) : LibC::Int
2018-11-20 11:06:15 +01:00
fun ipc_connection_copy(Connection*) : Connection*
fun ipc_connection_eq(Connection*, Connection*) : LibC::Int
2018-11-20 11:06:15 +01:00
fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt)
2018-11-20 11:06:15 +01:00
fun ipc_connections_free(Connections*)
fun ipc_get(Connections*)
fun ipc_errors_get (LibC::Int) : LibC::Char*
2018-11-20 11:06:15 +01:00
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
2019-06-04 01:36:21 +02:00
end
def initialize(mtype, type, payload : String)
initialize(mtype, type, Bytes.new(payload.to_unsafe, payload.bytesize))
2018-11-20 11:06:15 +01:00
end
end
class IPC::Event
class Connection
getter connection : IPC::Connection
def initialize(@connection)
2018-11-20 11:06:15 +01:00
end
end
class Disconnection
getter connection : IPC::Connection
def initialize(@connection)
2018-11-20 11:06:15 +01:00
end
end
class Message
getter message : ::IPC::Message
getter connection : IPC::Connection
def initialize(@message, @connection)
2018-11-20 11:06:15 +01:00
end
end
class ExtraSocket < IPC::Event::Message
end
2018-11-20 11:06:15 +01:00
end
class IPC::Connection
2019-06-06 00:37:12 +02:00
getter connection : LibIPC::Connection
getter closed = false
2018-11-20 11:06:15 +01:00
# 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}"
2018-11-20 11:06:15 +01:00
end
end
2018-11-20 11:06:15 +01:00
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
2018-11-20 11:06:15 +01:00
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}"
2018-11-20 11:06:15 +01:00
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
2018-11-20 11:06:15 +01:00
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}"
2018-11-20 11:06:15 +01:00
end
2019-06-06 00:37:12 +02:00
IPC::Message.new message.type, message.user_type, String.new(message.payload)
2018-11-20 11:06:15 +01:00
end
def close
return if @closed
r = LibIPC.ipc_close(pointerof(@connection))
if r != 0
m = String.new LibIPC.ipc_errors_get (r)
2019-06-06 00:37:12 +02:00
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
2019-06-06 00:37:12 +02:00
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
2019-06-06 00:37:12 +02:00
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
2018-11-20 11:06:15 +01:00
end
end
end