v0.2 - new API
This commit is contained in:
parent
dab7e4adec
commit
0330f384ef
@ -1,13 +1,14 @@
|
|||||||
name: ipc
|
name: ipc
|
||||||
version: 0.1.0
|
version: 0.2.0
|
||||||
|
|
||||||
authors:
|
authors:
|
||||||
|
- Philippe Pittoli <karchnu@karchnu.fr>
|
||||||
- Luka Vandervelden <lukc@upyum.com>
|
- Luka Vandervelden <lukc@upyum.com>
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
High-level Crystal bindings to libipc.
|
High-level Crystal bindings to libipc.
|
||||||
|
|
||||||
libraries:
|
libraries:
|
||||||
libipc: ">= 0.1"
|
libipc: ">= 0.1"
|
||||||
|
|
||||||
license: MIT
|
license: ISC
|
||||||
|
376
src/ipc.cr
376
src/ipc.cr
@ -1,371 +1,11 @@
|
|||||||
# TODO: more typing stuff.
|
# TODO: more typing stuff.
|
||||||
# Functions return enum error not just int, for instance.
|
# Functions return enum error not just int, for instance.
|
||||||
|
|
||||||
@[Link("ipc")]
|
require "./ipc/lowlevel.cr"
|
||||||
lib LibIPC
|
require "./ipc/exception.cr"
|
||||||
struct Connection
|
require "./ipc/message.cr"
|
||||||
version : LibC::UInt
|
require "./ipc/connection.cr"
|
||||||
index : LibC::UInt
|
require "./ipc/event.cr"
|
||||||
fd : LibC::Int
|
require "./ipc/client.cr"
|
||||||
type : UInt8
|
require "./ipc/service.cr"
|
||||||
spath : LibC::Char* # [4096] # [PATH_MAX]
|
require "./ipc/switch.cr"
|
||||||
end
|
|
||||||
|
|
||||||
struct Connections
|
|
||||||
cinfos : Connection**
|
|
||||||
size : LibC::Int
|
|
||||||
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
|
|
||||||
|
|
||||||
struct Message
|
|
||||||
type : UInt8
|
|
||||||
user_type : UInt8
|
|
||||||
length : LibC::UInt
|
|
||||||
payload : LibC::Char*
|
|
||||||
end
|
|
||||||
|
|
||||||
enum EventType
|
|
||||||
NotSet
|
|
||||||
Error
|
|
||||||
ExtraSocket
|
|
||||||
Connection
|
|
||||||
Disconnection
|
|
||||||
Message
|
|
||||||
end
|
|
||||||
|
|
||||||
struct Event
|
|
||||||
type : EventType
|
|
||||||
origin : Connection*
|
|
||||||
message : Message*
|
|
||||||
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
|
|
||||||
|
|
||||||
# connection to a service
|
|
||||||
fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int
|
|
||||||
|
|
||||||
fun ipc_read(Connection*, Message*) : LibC::Int
|
|
||||||
fun ipc_write(Connection*, Message*) : LibC::Int
|
|
||||||
|
|
||||||
fun ipc_wait_event(Connections*, Connection*, Event*) : 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_connection_copy(Connection*) : Connection*
|
|
||||||
fun ipc_connection_eq(Connection*, Connection*) : LibC::Int
|
|
||||||
|
|
||||||
fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt)
|
|
||||||
|
|
||||||
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::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
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(mtype, type, payload : String)
|
|
||||||
initialize(mtype, type, Bytes.new(payload.to_unsafe, payload.bytesize))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class IPC::Event
|
|
||||||
class Connection
|
|
||||||
getter connection : IPC::Connection
|
|
||||||
def initialize(@connection)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Disconnection
|
|
||||||
getter connection : IPC::Connection
|
|
||||||
def initialize(@connection)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Message
|
|
||||||
getter message : ::IPC::Message
|
|
||||||
getter connection : IPC::Connection
|
|
||||||
def initialize(@message, @connection)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ExtraSocket < IPC::Event::Message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
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, 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)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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 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
|
|
||||||
|
|
||||||
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}"
|
|
||||||
end
|
|
||||||
|
|
||||||
IPC::Message.new pointerof(message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
return if @closed
|
|
||||||
|
|
||||||
r = LibIPC.ipc_close(pointerof(@connection))
|
|
||||||
if r != 0
|
|
||||||
m = String.new LibIPC.ipc_errors_get (r)
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
43
src/ipc/client.cr
Normal file
43
src/ipc/client.cr
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
require "./lowlevel"
|
||||||
|
require "./message"
|
||||||
|
require "./event"
|
||||||
|
require "./service"
|
||||||
|
require "./connection"
|
||||||
|
|
||||||
|
|
||||||
|
class IPC::Client < IPC::Connections
|
||||||
|
@connection : IPC::Connection
|
||||||
|
|
||||||
|
def initialize(name : String)
|
||||||
|
super()
|
||||||
|
@connection = IPC::Connection.new name
|
||||||
|
self << @connection
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(name : String, &block : Proc(Events|Exception, Nil))
|
||||||
|
initialize name
|
||||||
|
::loop &block
|
||||||
|
close
|
||||||
|
end
|
||||||
|
|
||||||
|
def send(*args)
|
||||||
|
@connection.send *args
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(*args)
|
||||||
|
@connection.read *args
|
||||||
|
end
|
||||||
|
|
||||||
|
# sanitizer
|
||||||
|
def fd
|
||||||
|
@connection.fd
|
||||||
|
end
|
||||||
|
|
||||||
|
def loop(&block : Proc(Events|Exception, Nil))
|
||||||
|
super(nil, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
@connection.close
|
||||||
|
end
|
||||||
|
end
|
202
src/ipc/connection.cr
Normal file
202
src/ipc/connection.cr
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
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 != 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)
|
||||||
|
|
||||||
|
yield self
|
||||||
|
|
||||||
|
close
|
||||||
|
end
|
||||||
|
|
||||||
|
# sanitizer
|
||||||
|
def fd
|
||||||
|
@connection.fd
|
||||||
|
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
|
||||||
|
|
||||||
|
r = LibIPC.ipc_write(self.pointer, pointerof(message))
|
||||||
|
if r != 0
|
||||||
|
m = String.new LibIPC.ipc_errors_get (r)
|
||||||
|
raise Exception.new "error writing a message: #{m}"
|
||||||
|
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
|
||||||
|
|
||||||
|
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}"
|
||||||
|
end
|
||||||
|
|
||||||
|
IPC::Message.new pointerof(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
return if @closed
|
||||||
|
|
||||||
|
r = LibIPC.ipc_close(self.pointer)
|
||||||
|
if r != 0
|
||||||
|
m = String.new LibIPC.ipc_errors_get (r)
|
||||||
|
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
|
||||||
|
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 != 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(self.pointer, fd)
|
||||||
|
if r != 0
|
||||||
|
m = String.new LibIPC.ipc_errors_get (r)
|
||||||
|
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 != 0
|
||||||
|
m = String.new LibIPC.ipc_errors_get (r)
|
||||||
|
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 != 0
|
||||||
|
m = String.new LibIPC.ipc_errors_get (r)
|
||||||
|
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)
|
||||||
|
event = LibIPC::Event.new
|
||||||
|
|
||||||
|
serverp = nil
|
||||||
|
unless server.nil?
|
||||||
|
serverp = server.pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
r = LibIPC.ipc_wait_event self.pointer, serverp, 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
|
||||||
|
|
||||||
|
eventtype = event.type.unsafe_as(LibIPC::EventType)
|
||||||
|
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(Events|Exception, Nil))
|
||||||
|
::loop do
|
||||||
|
type, message, connection = wait_event server, &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::Switch
|
||||||
|
yield IPC::Event::Switch.new message, connection
|
||||||
|
|
||||||
|
when LibIPC::EventType::Message
|
||||||
|
yield IPC::Event::Message.new message, connection
|
||||||
|
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# sanitizer
|
||||||
|
def pointer
|
||||||
|
pointerof(@connections)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pp
|
||||||
|
LibIPC.ipc_connections_print @connections
|
||||||
|
end
|
||||||
|
end
|
36
src/ipc/event.cr
Normal file
36
src/ipc/event.cr
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
require "./lowlevel"
|
||||||
|
require "./message"
|
||||||
|
require "./connection"
|
||||||
|
|
||||||
|
class IPC::Event
|
||||||
|
class Connection
|
||||||
|
getter connection : IPC::Connection
|
||||||
|
def initialize(@connection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Disconnection
|
||||||
|
getter connection : IPC::Connection
|
||||||
|
def initialize(@connection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Message
|
||||||
|
getter message : ::IPC::Message
|
||||||
|
getter connection : IPC::Connection
|
||||||
|
def initialize(@message, @connection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ExtraSocket < IPC::Event::Message
|
||||||
|
end
|
||||||
|
|
||||||
|
class Switch < IPC::Event::Message
|
||||||
|
end
|
||||||
|
|
||||||
|
class LookUp < IPC::Event::Message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias Events = IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Event::ExtraSocket | IPC::Event::Switch | IPC::Event::LookUp
|
||||||
|
|
3
src/ipc/exception.cr
Normal file
3
src/ipc/exception.cr
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
class IPC::Exception < ::Exception
|
||||||
|
end
|
167
src/ipc/lowlevel.cr
Normal file
167
src/ipc/lowlevel.cr
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
|
||||||
|
@[Link("ipc")]
|
||||||
|
lib LibIPC
|
||||||
|
struct Connection
|
||||||
|
version : LibC::UInt
|
||||||
|
index : LibC::UInt
|
||||||
|
fd : LibC::Int
|
||||||
|
type : UInt8
|
||||||
|
spath : LibC::Char* # [4096] # [PATH_MAX]
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Connections
|
||||||
|
cinfos : Connection**
|
||||||
|
size : LibC::Int
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Switching
|
||||||
|
origin : LibC::Int
|
||||||
|
dest : LibC::Int
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Switchings
|
||||||
|
collection : Switching*
|
||||||
|
size : LibC::UInt
|
||||||
|
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
|
||||||
|
UsockConnectConnect
|
||||||
|
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
|
||||||
|
LookUp
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Message
|
||||||
|
type : UInt8
|
||||||
|
user_type : UInt8
|
||||||
|
length : LibC::UInt
|
||||||
|
payload : LibC::Char*
|
||||||
|
end
|
||||||
|
|
||||||
|
enum EventType
|
||||||
|
NotSet
|
||||||
|
Error
|
||||||
|
ExtraSocket
|
||||||
|
Switch
|
||||||
|
Connection
|
||||||
|
Disconnection
|
||||||
|
Message
|
||||||
|
LookUp
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Event
|
||||||
|
type : EventType
|
||||||
|
origin : Connection*
|
||||||
|
message : Message*
|
||||||
|
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
|
||||||
|
|
||||||
|
# connection to a service
|
||||||
|
fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : LibC::Int
|
||||||
|
|
||||||
|
fun ipc_read(Connection*, Message*) : LibC::Int
|
||||||
|
fun ipc_write(Connection*, Message*) : LibC::Int
|
||||||
|
|
||||||
|
fun ipc_wait_event(Connections*, Connection*, Event*) : 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_del_fd(Connections*, LibC::Int) : LibC::Int
|
||||||
|
|
||||||
|
fun ipc_connection_copy(Connection*) : Connection*
|
||||||
|
fun ipc_connection_eq(Connection*, Connection*) : LibC::Int
|
||||||
|
|
||||||
|
fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt)
|
||||||
|
|
||||||
|
fun ipc_connections_free(Connections*)
|
||||||
|
fun ipc_connections_close(Connections*)
|
||||||
|
fun ipc_get(Connections*)
|
||||||
|
fun ipc_errors_get (LibC::Int) : LibC::Char*
|
||||||
|
|
||||||
|
|
||||||
|
# networkd-related functions
|
||||||
|
fun ipc_wait_event_networkd(Connections*, Connection*, Event*, Switchings*) : LibC::Int
|
||||||
|
|
||||||
|
fun ipc_receive_fd (sock : LibC::Int, fd : LibC::Int*) : LibC::Int
|
||||||
|
fun ipc_provide_fd (sock : LibC::Int, fd : LibC::Int) : LibC::Int
|
||||||
|
|
||||||
|
fun ipc_switching_add (switch : Switchings*, fd1 : LibC::Int, fd2 : LibC::Int)
|
||||||
|
fun ipc_switching_del (switch : Switchings*, fd : LibC::Int ) : LibC::Int
|
||||||
|
fun ipc_switching_get (switch : Switchings*, fd : LibC::Int ) : LibC::Int
|
||||||
|
fun ipc_switching_free (switch : Switchings* ) : LibC::Int
|
||||||
|
fun ipc_switching_print (switch : Switchings*)
|
||||||
|
|
||||||
|
# non public functions (for testing purposes)
|
||||||
|
fun service_path (path : LibC::Char*, sname : LibC::Char*, index : Int32, version : Int32) : LibC::Int
|
||||||
|
fun log_get_logfile_dir (buf : LibC::Char*, size : LibC::UInt) : LibC::Char*
|
||||||
|
fun log_get_logfile_name (buf : LibC::Char*, size : LibC::UInt)
|
||||||
|
fun ipc_connections_print(Connections*)
|
||||||
|
end
|
40
src/ipc/message.cr
Normal file
40
src/ipc/message.cr
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
require "./lowlevel"
|
||||||
|
|
||||||
|
class IPC::Message
|
||||||
|
getter mtype : UInt8 # libipc message type
|
||||||
|
property type : UInt8 # libipc user message type
|
||||||
|
property 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(message : LibIPC::Message)
|
||||||
|
initialize pointerof(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(mtype, type, payload : Bytes)
|
||||||
|
@mtype = mtype.to_u8
|
||||||
|
@type = type
|
||||||
|
@payload = payload
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(mtype, type, payload : String)
|
||||||
|
initialize(mtype, type, Bytes.new(payload.to_unsafe, payload.bytesize))
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"(internal) type #{@mtype}, (user) type #{@type}, payload #{String.new @payload}"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
92
src/ipc/service.cr
Normal file
92
src/ipc/service.cr
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
require "./lowlevel"
|
||||||
|
require "./message"
|
||||||
|
require "./event"
|
||||||
|
require "./connection"
|
||||||
|
|
||||||
|
# the server is a connection with two different function calls
|
||||||
|
# ipc_connection => ipc_server_init
|
||||||
|
# ipc_close => ipc_server_close
|
||||||
|
class IPC::Server < IPC::Connection
|
||||||
|
def initialize(name : String)
|
||||||
|
@connection = LibIPC::Connection.new
|
||||||
|
r = LibIPC.ipc_server_init(LibC.environ, self.pointer, 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 close
|
||||||
|
return if @closed
|
||||||
|
|
||||||
|
r = LibIPC.ipc_server_close(self.pointer)
|
||||||
|
if r != 0
|
||||||
|
m = String.new LibIPC.ipc_errors_get (r)
|
||||||
|
raise Exception.new "cannot close the server correctly: #{m}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@closed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class IPC::Service < IPC::Connections
|
||||||
|
@service_info : IPC::Server
|
||||||
|
|
||||||
|
def initialize(name : String)
|
||||||
|
@service_info = IPC::Server.new name
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(name : String, &block : Proc(Events|Exception, Nil))
|
||||||
|
initialize name
|
||||||
|
loop &block
|
||||||
|
close
|
||||||
|
end
|
||||||
|
|
||||||
|
# sanitizer
|
||||||
|
def fd
|
||||||
|
@service_info.fd
|
||||||
|
end
|
||||||
|
|
||||||
|
def loop(&block : Proc(Events|Exception, Nil))
|
||||||
|
super(@service_info, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
@service_info.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: replacing IPC::Service by the IPC::NetworkD class?
|
||||||
|
class IPC::SwitchingService < IPC::Service
|
||||||
|
property switch = IPC::Switch.new
|
||||||
|
|
||||||
|
# automatic removal of the fd in the switching list
|
||||||
|
def remove_fd (fd : Int)
|
||||||
|
super
|
||||||
|
@switch.del fd
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_event(server : IPC::Connection , &block) : Tuple(LibIPC::EventType, IPC::Message, IPC::Connection)
|
||||||
|
event = LibIPC::Event.new
|
||||||
|
|
||||||
|
serverp = server.pointer
|
||||||
|
r = LibIPC.ipc_wait_event_networkd self.pointer, serverp, pointerof(event), @switch.pointer
|
||||||
|
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
|
||||||
|
|
||||||
|
message = event.message.unsafe_as(Pointer(LibIPC::Message))
|
||||||
|
|
||||||
|
eventtype = event.type.unsafe_as(LibIPC::EventType)
|
||||||
|
|
||||||
|
return eventtype, IPC::Message.new(message), connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
33
src/ipc/switch.cr
Normal file
33
src/ipc/switch.cr
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
require "./lowlevel"
|
||||||
|
|
||||||
|
class IPC::Switch
|
||||||
|
@switch = LibIPC::Switchings.new
|
||||||
|
|
||||||
|
def inilialize
|
||||||
|
end
|
||||||
|
|
||||||
|
def add (fd1 : Int32, fd2 : Int32)
|
||||||
|
LibIPC.ipc_switching_add self.pointer, fd1, fd2
|
||||||
|
end
|
||||||
|
|
||||||
|
def del (fd : Int32)
|
||||||
|
LibIPC.ipc_switching_del self.pointer, fd
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
LibIPC.ipc_switching_free self.pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
def print
|
||||||
|
LibIPC.ipc_switching_print self.pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize
|
||||||
|
close
|
||||||
|
end
|
||||||
|
|
||||||
|
# sanitizer
|
||||||
|
def pointer
|
||||||
|
pointerof(@switch)
|
||||||
|
end
|
||||||
|
end
|
Reference in New Issue
Block a user