Archived
3
0

Bindings following libipc version 0.7.0.

This commit is contained in:
Karchnu 2020-07-13 14:17:29 +02:00
parent 485d26d677
commit d1c3e9a95a
12 changed files with 355 additions and 464 deletions

View File

@ -1,5 +1,5 @@
name: ipc name: ipc
version: 0.6.0 version: 0.7.0
authors: authors:
- Philippe Pittoli <karchnu@karchnu.fr> - Philippe Pittoli <karchnu@karchnu.fr>
@ -9,6 +9,6 @@ description: |
High-level Crystal bindings to libipc. High-level Crystal bindings to libipc.
libraries: libraries:
libipc: ">= 0.5" libipc: ">= 0.7"
license: ISC license: ISC

View File

@ -1,11 +1,8 @@
# TODO: more typing stuff.
# Functions return enum error not just int, for instance.
require "./ipc/lowlevel.cr" require "./ipc/lowlevel.cr"
require "./ipc/exception.cr" require "./ipc/exception.cr"
require "./ipc/message.cr" require "./ipc/message.cr"
require "./ipc/connection.cr"
require "./ipc/event.cr" require "./ipc/event.cr"
require "./ipc/client.cr"
require "./ipc/service.cr"
require "./ipc/switch.cr" require "./ipc/switch.cr"
require "./ipc/context.cr"
require "./ipc/client.cr"
require "./ipc/server.cr"

View File

@ -1,43 +1,15 @@
require "./lowlevel"
require "./message"
require "./event"
require "./service"
require "./connection"
class IPC::Client < IPC::Context
class IPC::Client < IPC::Connections # By default, this is a client.
@connection : IPC::Connection def initialize(service_name : String)
def initialize(name : String)
super() super()
@connection = IPC::Connection.new name r = LibIPC.ipc_connection(self.pointer, service_name)
self << @connection 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 : String, &block : Proc(IPC::Event::Events|Exception, Nil)) # Very important as there are filesystem side-effects.
initialize name at_exit { close }
::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(IPC::Event::Events|Exception, Nil))
super(nil, &block)
end
def close
@connection.close
end end
end end

View File

@ -1,230 +0,0 @@
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

152
src/ipc/context.cr Normal file
View File

@ -0,0 +1,152 @@
class IPC::Context
property base_timer : Int32 = LibIPC::INFTIM
property timer : Int32 = LibIPC::INFTIM
getter context : LibIPC::Ctx
def initialize
@context = LibIPC::Ctx.new
end
def initialize(@context : LibIPC::Ctx)
end
def initialize(name : String, &block : Proc(IPC::Event::Events|Exception, Nil))
initialize name
::loop &block
close
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_index (index : UInt32)
r = LibIPC.ipc_del(self.pointer, index)
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 remove_fd (fd : Int32)
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_wait_event 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.origin, event.index
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.
lowlevel_message = event.message.unsafe_as(Pointer(LibIPC::Message))
ipc_message = IPC::Message.new lowlevel_message
return IPC::Event::MessageReceived.new event.origin, event.index, ipc_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::MessageSent.new event.origin, event.index
end
return Exception.new "Cannot understand the event type: #{eventtype}"
end
def loop(&block : Proc(IPC::Event::Events|Exception, Nil))
::loop do
if @base_timer > 0 && @timer == 0
@timer = @base_timer
end
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(message : IPC::Message)
send(message.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
# 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
LibIPC.ipc_ctx_free(self.pointer)
@closed = true
end
def pp
LibIPC.ipc_ctx_print self.pointer
end
end

View File

@ -1,39 +1,54 @@
require "./lowlevel"
require "./message"
require "./connection"
class IPC::Event class IPC::Event
alias Events = IPC::Event::Timer | IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Event::ExtraSocket | IPC::Event::Switch | IPC::Event::LookUp alias Events = IPC::Event::Timer |
IPC::Event::Error |
IPC::Event::Connection |
IPC::Event::Disconnection |
IPC::Event::MessageReceived |
IPC::Event::ExtraSocket |
IPC::Event::Switch |
IPC::Event::LookUp |
IPC::Event::MessageSent
end
class Timer class IPC::Event::Timer < IPC::Event
end def initialize
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
end end
class IPC::Event::Base < IPC::Event
property fd : Int32
property index : UInt32
def initialize(@fd, @index)
end
end
class IPC::Event::Connection < IPC::Event::Base
end
class IPC::Event::Disconnection < IPC::Event::Base
end
class IPC::Event::Error < IPC::Event::Base
end
class IPC::Event::MessageReceived < IPC::Event::Base
getter message : ::IPC::Message
def initialize(@fd, @index, @message)
end
end
class IPC::Event::ExtraSocket < IPC::Event::Base
end
class IPC::Event::Switch < IPC::Event::Base
end
class IPC::Event::LookUp < IPC::Event::Base
end
class IPC::Event::MessageSent < IPC::Event::Base
end

View File

@ -1,22 +1,33 @@
@[Link("ipc")] @[Link("ipc")]
lib LibIPC lib LibIPC
INFTIM = -1
enum ConnectionType
IPC # IO op. are handled by libipc.
External # IO op. are handled by the libipc user app.
Server # Should listen and accept new IPC users.
Switched # IO op. are handled by callbacks.
end
struct Connection struct Connection
version : LibC::UInt type : ConnectionType #
index : LibC::UInt
fd : LibC::Int
type : UInt8
spath : LibC::Char* # [4096] # [PATH_MAX] spath : LibC::Char* # [4096] # [PATH_MAX]
end end
struct Connections struct Pollfd
cinfos : Connection** fd : LibC::Int
size : LibC::Int events : LibC::Short
revents : LibC::Short
end end
struct Switching struct Switching
origin : LibC::Int origin : LibC::Int
dest : LibC::Int dest : LibC::Int
orig_cb_in : (Int32, Pointer(Message)) -> ConnectionType
orig_cb_out : (Int32, Pointer(Message)) -> ConnectionType
dest_cb_in : (Int32, Pointer(Message)) -> ConnectionType
dest_cb_out : (Int32, Pointer(Message)) -> ConnectionType
end end
struct Switchings struct Switchings
@ -24,6 +35,14 @@ lib LibIPC
size : LibC::UInt size : LibC::UInt
end end
struct Ctx
cinfos : Connection*
pollfd : Pollfd*
size : LibC::UInt64T
tx : Messages
switchdb : Switchings
end
enum MessageType enum MessageType
ServerClose ServerClose
Error Error
@ -31,29 +50,38 @@ lib LibIPC
LookUp LookUp
end end
# Messages are stored in lists within the libipc before being sent.
struct Messages
messages : Message*
size : LibC::UInt64T
end
struct Message struct Message
type : UInt8 type : UInt8 # Internal message type.
user_type : UInt8 user_type : UInt8 # User-defined message type.
length : LibC::UInt fd : LibC::Int # fd of the sender.
payload : LibC::Char* length : LibC::UInt # Payload length.
payload : LibC::Char* #
end end
enum EventType enum EventType
NotSet NotSet #
Error Error #
ExtraSocket ExtraSocket # Message received from a non IPC socket.
Switch Switch # Message to send to a corresponding fd.
Connection Connection # New user.
Disconnection Disconnection # User disconnected.
Message Message # New message.
LookUp LookUp # Client asking for a service through ipcd.
Timer Timer # Timeout in the poll(2) function.
Tx # Message sent.
end end
struct Event struct Event
type : EventType type : EventType #
origin : Connection* index : LibC::UInt # Index of the sender in the ipc_ctx structure.
message : Message* origin : LibC::Int # fd of the sender.
message : Message* # Pointer to the reveiced message.
end end
struct IPCError struct IPCError
@ -62,46 +90,72 @@ lib LibIPC
error_message : LibC::Char[8192] error_message : LibC::Char[8192]
end end
fun ipc_server_init(env : LibC::Char**, connection : Connection*, sname : LibC::Char*) : IPCError # Connection functions.
fun ipc_server_close(Connection*) : IPCError # Context is allocated, ipcd is requested and the connection/initialisation is performed.
fun ipc_close(Connection*) : IPCError fun ipc_server_init(ctx : Ctx*, sname : LibC::Char*) : IPCError
fun ipc_connection(Ctx*, LibC::Char*) : IPCError
fun ipc_connection_switched(Ctx*, LibC::Char*, LibC::Int, Pointer(LibC::Int)) : IPCError
# connection to a service # ipc_message_copy: pm, @fd, @mtype, @utype, @payload
fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : IPCError fun ipc_message_copy(Message*, LibC::Int, UInt8, UInt8, LibC::Char*, Int32)
fun ipc_read(Connection*, Message*) : IPCError # Closing connections.
fun ipc_write(Connection*, Message*) : IPCError fun ipc_close(ctx : Ctx*, index : LibC::UInt64T) : IPCError
fun ipc_close_all(ctx : Ctx*) : IPCError
fun ipc_wait_event(Connections*, Connection*, Event*, LibC::Double*) : IPCError fun ipc_ctx_free(Ctx*) # Void
fun ipc_add(Connections*, Connection*) : IPCError # Loop function.
fun ipc_del(Connections*, Connection*) : IPCError fun ipc_wait_event(Ctx*, Event*, LibC::Int*) : IPCError
fun ipc_add_fd(Connections*, LibC::Int) : IPCError
fun ipc_del_fd(Connections*, LibC::Int) : IPCError
fun ipc_connection_gen(Connection*, LibC::UInt, LibC::UInt) : IPCError # Adding and removing file discriptors to read.
fun ipc_add(Ctx*, Connection*, Pollfd*) : IPCError
fun ipc_del(Ctx*, LibC::UInt) : IPCError
fun ipc_add_fd(Ctx*, LibC::Int) : IPCError
fun ipc_add_fd_switched(Ctx*, LibC::Int) : IPCError
fun ipc_del_fd(Ctx*, LibC::Int) : IPCError
fun ipc_connections_free(Connections*) # Void # Sending a message (will wait the fd to become available for IO operations).
fun ipc_connections_close(Connections*) # Void fun ipc_write(Ctx*, Message*) : IPCError
# This function let the user get the default error message based on the error code. # This function let the user get the default error message based on the error code.
# The error message is contained in the IPCError structure, this function should not be used, in most cases. # The error message is contained in the IPCError structure, this function should not be used, in most cases.
fun ipc_errors_get (LibC::Int) : LibC::Char* fun ipc_errors_get (LibC::Int) : LibC::Char*
# Exchanging file descriptors (used with ipcd on connection).
# networkd-related functions
fun ipc_wait_event_networkd(Connections*, Connection*, Event*, Switchings*, LibC::Double*) : IPCError
fun ipc_receive_fd (sock : LibC::Int, fd : LibC::Int*) : IPCError fun ipc_receive_fd (sock : LibC::Int, fd : LibC::Int*) : IPCError
fun ipc_provide_fd (sock : LibC::Int, fd : LibC::Int ) : IPCError fun ipc_provide_fd (sock : LibC::Int, fd : LibC::Int ) : IPCError
# To change the type of a fd.
fun ipc_ctx_fd_type(Ctx*, LibC::Int, LibIPC::ConnectionType) : LibC::Int
enum IPCCB
NoError #
Closing #
Error #
ParsingError #
Ignore #
end
# Changing the callbacks for switched fd.
# ipc_switching_callbacks: ctx, fd
# , enum ipccb cb_in (fd, *ipc_message)
# , enum ipccb cb_out (fd, *ipc_message)
fun ipc_switching_callbacks(Ctx*, LibC::Int,
(LibC::Int, LibIPC::Message* -> LibIPC::IPCCB),
(LibC::Int, LibIPC::Message* -> LibIPC::IPCCB))
fun ipc_ctx_switching_add (ctx : Ctx*, fd1 : LibC::Int, fd2 : LibC::Int) # Void
fun ipc_switching_add (switch : Switchings*, fd1 : LibC::Int, fd2 : LibC::Int) # Void fun ipc_switching_add (switch : Switchings*, fd1 : LibC::Int, fd2 : LibC::Int) # Void
fun ipc_switching_del (switch : Switchings*, fd : LibC::Int ) : 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_get (switch : Switchings*, fd : LibC::Int ) : LibC::Int
fun ipc_switching_free (switch : Switchings* ) # Void fun ipc_switching_free (switch : Switchings* ) # Void
# non public functions (for testing purposes) # non public functions
fun ipc_read(ctx : Ctx*, index : LibC::UInt, message : Message*) : IPCError
# for testing purposes
fun ipc_switching_print (switch : Switchings*) # Void fun ipc_switching_print (switch : Switchings*) # Void
fun service_path (path : LibC::Char*, sname : LibC::Char*, index : Int32, version : Int32) : IPCError fun service_path (path : LibC::Char*, sname : LibC::Char*, index : Int32, version : Int32) : IPCError
fun ipc_connections_print (Connections*) # Void fun ipc_ctx_print (Ctx*) # Void
end end

View File

@ -1,11 +1,10 @@
require "./lowlevel"
require "json" require "json"
# JSON is currently used for messages over websockets # JSON is currently used for messages over websockets
# At some point, this will be replaced by the CBOR format # At some point, this will be replaced by the CBOR format
class IPC::Message class IPC::Message
property fd : Int32 # file descriptor
property mtype : UInt8 # libipc message type property mtype : UInt8 # libipc message type
property utype : UInt8 # libipc user message type property utype : UInt8 # libipc user message type
property payload : Bytes property payload : Bytes
@ -24,7 +23,7 @@ class IPC::Message
def self.from_json (str : String) def self.from_json (str : String)
jsonmessage = JSONMessage.from_json str jsonmessage = JSONMessage.from_json str
IPC::Message.new jsonmessage.mtype, jsonmessage.utype, jsonmessage.payload IPC::Message.new 0, jsonmessage.mtype, jsonmessage.utype, jsonmessage.payload
end end
def to_json def to_json
@ -34,10 +33,12 @@ class IPC::Message
def initialize(message : Pointer(LibIPC::Message)) def initialize(message : Pointer(LibIPC::Message))
if message.null? if message.null?
@mtype = LibIPC::MessageType::Error.to_u8 @mtype = LibIPC::MessageType::Error.to_u8
@fd = 0
@utype = 0 @utype = 0
@payload = Bytes.new "".to_unsafe, 0 @payload = Bytes.new "".to_unsafe, 0
else else
m = message.value m = message.value
@fd = m.fd
@mtype = m.type @mtype = m.type
@utype = m.user_type @utype = m.user_type
@payload = Bytes.new m.payload, m.length @payload = Bytes.new m.payload, m.length
@ -48,14 +49,12 @@ class IPC::Message
initialize pointerof(message) initialize pointerof(message)
end end
def initialize(mtype, utype, payload : Bytes) def initialize(@fd, mtype, @utype, @payload : Bytes)
@mtype = mtype.to_u8 @mtype = mtype.to_u8
@utype = utype
@payload = payload
end end
def initialize(mtype, utype, payload : String) def initialize(fd, mtype, utype, payload : String)
initialize(mtype, utype, Bytes.new(payload.to_unsafe, payload.bytesize)) initialize(fd, mtype, utype, Bytes.new(payload.to_unsafe, payload.bytesize))
end end
def self.to_packet (user_type : Int, message : String) def self.to_packet (user_type : Int, message : String)
@ -76,6 +75,10 @@ class IPC::Message
IPC::Message.to_packet @utype, String.new(@payload) IPC::Message.to_packet @utype, String.new(@payload)
end end
def copy_to_message_pointer(pm : LibIPC::Message*)
LibIPC.ipc_message_copy pm, @fd, @mtype, @utype, @payload, @payload.size
end
def to_s def to_s
"(internal) utype #{@mtype}, (user) utype #{@utype}, payload #{String.new @payload}" "(internal) utype #{@mtype}, (user) utype #{@utype}, payload #{String.new @payload}"
end end

28
src/ipc/server.cr Normal file
View File

@ -0,0 +1,28 @@
# the server is a client with a different init function
# ipc_connection => ipc_server_init
class IPC::Server < IPC::Context
def initialize(name : String)
initialize()
r = LibIPC.ipc_server_init(self.pointer, name)
if r.error_code != 0
m = String.new r.error_message.to_slice
raise Exception.new "cannot initialize the server named #{name}: #{m}"
end
# Very important as there are filesystem side-effects.
at_exit { close }
end
end
# TODO: replacing IPC::Service by the IPC::NetworkD class?
class IPC::SwitchingService < IPC::Server
property switch = IPC::Switch.new
# automatic removal of the fd in the switching list
def remove_fd (fd : Int)
super
@switch.del fd
end
end

View File

@ -1,99 +0,0 @@
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.error_code != 0
m = String.new r.error_message.to_slice
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.error_code != 0
m = String.new r.error_message.to_slice
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(IPC::Event::Events|Exception, Nil))
initialize name
loop &block
close
end
# sanitizer
def fd
@service_info.fd
end
def loop(&block : Proc(IPC::Event::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) | Tuple(LibIPC::EventType, Nil, Nil)
event = LibIPC::Event.new
serverp = server.pointer
r = LibIPC.ipc_wait_event_networkd self.pointer, serverp, pointerof(event), @switch.pointer, 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
end

View File

@ -1,4 +1,3 @@
require "./lowlevel"
class IPC::Switch class IPC::Switch
@switch = LibIPC::Switchings.new @switch = LibIPC::Switchings.new

View File

@ -25,7 +25,7 @@ class IPC::JSON
end end
end end
class IPC::Connection class IPC::Context
def send(message : IPC::JSON) def send(message : IPC::JSON)
send message.type.to_u8, message.to_json send message.type.to_u8, message.to_json
end end