Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
Philippe Pittoli | 7bb91b3509 | |
Karchnu | 93b9bcbaa2 | |
Karchnu | 21334a4d0a | |
Karchnu | 62d69388ee | |
Karchnu | 80fe0fa45d | |
Karchnu | f565dffbe3 | |
Karchnu | 30dbf2e85b | |
Karchnu | 89125d8338 | |
Karchnu | ee2852357e | |
Karchnu | fff35d53de | |
Karchnu | bc77e8c056 | |
Karchnu | 7870346afb | |
Karchnu | d791528008 | |
Karchnu | 6d5839b5a7 | |
Karchnu | 90e98c328a | |
Karchnu | 2505ec1c4a | |
Karchnu | 2ed077f3f9 | |
Karchnu | 50ac7152af | |
Karchnu | d1c3e9a95a |
15
shard.yml
15
shard.yml
|
@ -1,5 +1,5 @@
|
|||
name: ipc
|
||||
version: 0.6.0
|
||||
version: 0.7.0
|
||||
|
||||
authors:
|
||||
- Philippe Pittoli <karchnu@karchnu.fr>
|
||||
|
@ -8,7 +8,18 @@ authors:
|
|||
description: |
|
||||
High-level Crystal bindings to libipc.
|
||||
|
||||
dependencies:
|
||||
cbor:
|
||||
branch: master
|
||||
git: https://git.baguette.netlib.re/Baguette/crystal-cbor
|
||||
|
||||
targets:
|
||||
pongd:
|
||||
main: tests/pongd.cr
|
||||
pongc:
|
||||
main: tests/pongc.cr
|
||||
|
||||
libraries:
|
||||
libipc: ">= 0.5"
|
||||
libipc: ">= 0.7"
|
||||
|
||||
license: ISC
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
require "cbor"
|
||||
|
||||
require "./ipc.cr"
|
||||
|
||||
class IPC::CBOR
|
||||
include ::CBOR::Serializable
|
||||
|
||||
#@[::CBOR::Field(ignored: true)]
|
||||
#getter type = -1
|
||||
class_getter type = -1
|
||||
|
||||
property id : ::CBOR::Any?
|
||||
def type
|
||||
@@type
|
||||
end
|
||||
|
||||
macro message(id, type, &block)
|
||||
class {{id}} < ::IPC::CBOR
|
||||
include ::CBOR::Serializable
|
||||
|
||||
@@type = {{type}}
|
||||
|
||||
{{yield}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IPC::Context
|
||||
def send(fd : Int32, message : IPC::CBOR)
|
||||
send fd, message.type.to_u8, message.to_cbor
|
||||
end
|
||||
def send_now(fd : Int32, message : IPC::CBOR)
|
||||
send_now fd, message.type.to_u8, message.to_cbor
|
||||
end
|
||||
end
|
||||
|
||||
class IPC::Client
|
||||
def send(message : IPC::CBOR)
|
||||
send @server_fd.not_nil!, message.type.to_u8, message.to_cbor
|
||||
end
|
||||
def send_now(message : IPC::CBOR)
|
||||
send_now @server_fd.not_nil!, message.type.to_u8, message.to_cbor
|
||||
end
|
||||
end
|
||||
|
||||
# CAUTION: Only use this method on an Array(IPC::CBOR.class)
|
||||
class Array(T)
|
||||
def parse_ipc_cbor(message : IPC::Message) : IPC::CBOR?
|
||||
message_type = find &.type.==(message.utype)
|
||||
|
||||
if message_type.nil?
|
||||
raise "invalid message type (#{message.utype})"
|
||||
end
|
||||
|
||||
message_type.from_cbor message.payload
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
# TODO: more typing stuff.
|
||||
# Functions return enum error not just int, for instance.
|
||||
|
||||
require "./ipc/lowlevel.cr"
|
||||
require "./ipc/exception.cr"
|
||||
require "./ipc/message.cr"
|
||||
require "./ipc/connection.cr"
|
||||
require "./ipc/event.cr"
|
||||
require "./ipc/client.cr"
|
||||
require "./ipc/service.cr"
|
||||
require "./ipc/switch.cr"
|
||||
require "./ipc/context.cr"
|
||||
require "./ipc/client.cr"
|
||||
require "./ipc/server.cr"
|
||||
|
|
|
@ -1,43 +1,39 @@
|
|||
require "./lowlevel"
|
||||
require "./message"
|
||||
require "./event"
|
||||
require "./service"
|
||||
require "./connection"
|
||||
|
||||
class IPC::Client < IPC::Context
|
||||
property server_fd : Int32?
|
||||
|
||||
class IPC::Client < IPC::Connections
|
||||
@connection : IPC::Connection
|
||||
|
||||
def initialize(name : String)
|
||||
# By default, this is a client.
|
||||
def initialize(service_name : String)
|
||||
super()
|
||||
@connection = IPC::Connection.new name
|
||||
self << @connection
|
||||
serverfd = 0
|
||||
|
||||
r = LibIPC.ipc_connection(self.pointer, service_name, pointerof(serverfd))
|
||||
if r.error_code != 0
|
||||
m = String.new r.error_message.to_slice
|
||||
raise Exception.new "error during connection establishment: #{m}"
|
||||
end
|
||||
|
||||
@server_fd = serverfd
|
||||
|
||||
# Very important as there are filesystem side-effects.
|
||||
at_exit { close }
|
||||
end
|
||||
|
||||
def initialize(name : String, &block : Proc(IPC::Event::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
|
||||
@server_fd
|
||||
end
|
||||
|
||||
def loop(&block : Proc(IPC::Event::Events|Exception, Nil))
|
||||
super(nil, &block)
|
||||
end
|
||||
|
||||
def close
|
||||
@connection.close
|
||||
def read
|
||||
unless (fd = @server_fd).nil?
|
||||
message = LibIPC::Message.new
|
||||
r = LibIPC.ipc_read_fd(fd, 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)
|
||||
else
|
||||
raise "Client not connected to a server"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,192 @@
|
|||
|
||||
class IPC::Context
|
||||
# TODO: FIXME: base timer default is 30 seconds, INFTIM was causing trouble.
|
||||
property base_timer : Int32 = 30_000 # LibIPC::INFTIM
|
||||
property timer : Int32 = 30_000 # LibIPC::INFTIM
|
||||
getter context : LibIPC::Ctx
|
||||
|
||||
# On message reception, the message is contained into the event structure.
|
||||
# This message is automatically deallocated on the next ipc_wait_event call.
|
||||
# Therefore, we must keep this structure.
|
||||
property event = LibIPC::Event.new
|
||||
|
||||
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 : IPC::Event::Events | Exception
|
||||
r = LibIPC.ipc_wait_event self.pointer, pointerof(@event), pointerof(@timer)
|
||||
|
||||
event_type = @event.type.unsafe_as(LibIPC::EventType)
|
||||
|
||||
if r.error_code != 0
|
||||
m = String.new r.error_message.to_slice
|
||||
case event_type
|
||||
when LibIPC::EventType::Disconnection # User disconnected.
|
||||
# In this case, error_code may not be 0, disconnections can happen at any time.
|
||||
return IPC::Event::Disconnection.new @event.origin, @event.index
|
||||
end
|
||||
# Special case where the fd is closed.
|
||||
if r.error_code == 107
|
||||
# Baguette::Log.error "ERROR 107: fd #{@event.origin}"
|
||||
return IPC::Event::Disconnection.new @event.origin, @event.index
|
||||
else
|
||||
# Baguette::Log.error "ERROR #{r.error_code}: fd #{@event.origin}"
|
||||
end
|
||||
return IPC::Exception.new "error waiting for a new event: #{m}"
|
||||
end
|
||||
|
||||
# if event type is Timer, there is no file descriptor nor message
|
||||
case event_type
|
||||
when LibIPC::EventType::NotSet
|
||||
return IPC::Event::EventNotSet.new @event.origin, @event.index
|
||||
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: #{event_type}"
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
def send_now(message : LibIPC::Message)
|
||||
r = LibIPC.ipc_write_fd(message.fd, 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_now(message : IPC::Message)
|
||||
send_now fd: message.fd, utype: message.utype, payload: message.payload
|
||||
end
|
||||
|
||||
def send_now(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
|
||||
send_now message
|
||||
end
|
||||
|
||||
def send_now(fd : Int32, utype : UInt8, payload : String)
|
||||
send_now fd, utype, payload.to_slice
|
||||
end
|
||||
|
||||
def send(message : LibIPC::Message)
|
||||
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(message : IPC::Message)
|
||||
send fd: message.fd, utype: message.utype, payload: message.payload
|
||||
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
|
||||
send message
|
||||
end
|
||||
|
||||
def send(fd : Int32, utype : UInt8, payload : String)
|
||||
send fd, utype, payload.to_slice
|
||||
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
|
||||
|
||||
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
|
|
@ -1,39 +1,58 @@
|
|||
require "./lowlevel"
|
||||
require "./message"
|
||||
require "./connection"
|
||||
|
||||
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::EventNotSet |
|
||||
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
|
||||
end
|
||||
|
||||
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
|
||||
class IPC::Event::Timer < IPC::Event
|
||||
def initialize
|
||||
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
|
||||
|
||||
class IPC::Event::EventNotSet < IPC::Event::Base
|
||||
end
|
||||
|
||||
|
|
|
@ -1,22 +1,34 @@
|
|||
|
||||
@[Link("ipc")]
|
||||
lib LibIPC
|
||||
struct Connection
|
||||
version : LibC::UInt
|
||||
index : LibC::UInt
|
||||
fd : LibC::Int
|
||||
type : UInt8
|
||||
spath : LibC::Char* # [4096] # [PATH_MAX]
|
||||
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 Connections
|
||||
cinfos : Connection**
|
||||
size : LibC::Int
|
||||
struct Connection
|
||||
type : ConnectionType #
|
||||
more_to_read : Int16* #
|
||||
spath : LibC::Char* # [4096] # [PATH_MAX]
|
||||
end
|
||||
|
||||
struct Pollfd
|
||||
fd : LibC::Int
|
||||
events : LibC::Short
|
||||
revents : LibC::Short
|
||||
end
|
||||
|
||||
struct Switching
|
||||
origin : LibC::Int
|
||||
dest : LibC::Int
|
||||
orig_cb_in : (Int32, Pointer(Message), Int16*) -> ConnectionType
|
||||
orig_cb_out : (Int32, Pointer(Message)) -> ConnectionType
|
||||
dest_cb_in : (Int32, Pointer(Message), Int16*) -> ConnectionType
|
||||
dest_cb_out : (Int32, Pointer(Message)) -> ConnectionType
|
||||
end
|
||||
|
||||
struct Switchings
|
||||
|
@ -24,6 +36,14 @@ lib LibIPC
|
|||
size : LibC::UInt
|
||||
end
|
||||
|
||||
struct Ctx
|
||||
cinfos : Connection*
|
||||
pollfd : Pollfd*
|
||||
size : LibC::UInt64T
|
||||
tx : Messages
|
||||
switchdb : Switchings
|
||||
end
|
||||
|
||||
enum MessageType
|
||||
ServerClose
|
||||
Error
|
||||
|
@ -31,29 +51,38 @@ lib LibIPC
|
|||
LookUp
|
||||
end
|
||||
|
||||
# Messages are stored in lists within the libipc before being sent.
|
||||
struct Messages
|
||||
messages : Message*
|
||||
size : LibC::UInt64T
|
||||
end
|
||||
|
||||
struct Message
|
||||
type : UInt8
|
||||
user_type : UInt8
|
||||
length : LibC::UInt
|
||||
payload : LibC::Char*
|
||||
type : UInt8 # Internal message type.
|
||||
user_type : UInt8 # User-defined message type.
|
||||
fd : LibC::Int # fd of the sender.
|
||||
length : LibC::UInt # Payload length.
|
||||
payload : LibC::Char* #
|
||||
end
|
||||
|
||||
enum EventType
|
||||
NotSet
|
||||
Error
|
||||
ExtraSocket
|
||||
Switch
|
||||
Connection
|
||||
Disconnection
|
||||
Message
|
||||
LookUp
|
||||
Timer
|
||||
NotSet #
|
||||
Error #
|
||||
ExtraSocket # Message received from a non IPC socket.
|
||||
Switch # Message to send to a corresponding fd.
|
||||
Connection # New user.
|
||||
Disconnection # User disconnected.
|
||||
Message # New message.
|
||||
LookUp # Client asking for a service through ipcd.
|
||||
Timer # Timeout in the poll(2) function.
|
||||
Tx # Message sent.
|
||||
end
|
||||
|
||||
struct Event
|
||||
type : EventType
|
||||
origin : Connection*
|
||||
message : Message*
|
||||
type : EventType #
|
||||
index : LibC::UInt # Index of the sender in the ipc_ctx structure.
|
||||
origin : LibC::Int # fd of the sender.
|
||||
message : Message* # Pointer to the reveiced message.
|
||||
end
|
||||
|
||||
struct IPCError
|
||||
|
@ -62,46 +91,78 @@ lib LibIPC
|
|||
error_message : LibC::Char[8192]
|
||||
end
|
||||
|
||||
fun ipc_server_init(env : LibC::Char**, connection : Connection*, sname : LibC::Char*) : IPCError
|
||||
fun ipc_server_close(Connection*) : IPCError
|
||||
fun ipc_close(Connection*) : IPCError
|
||||
# Connection functions.
|
||||
# Context is allocated, ipcd is requested and the connection/initialisation is performed.
|
||||
fun ipc_server_init(ctx : Ctx*, sname : LibC::Char*) : IPCError
|
||||
fun ipc_connection(Ctx*, LibC::Char*, Int32*) : IPCError
|
||||
fun ipc_connection_switched(Ctx*, LibC::Char*, LibC::Int, Pointer(LibC::Int)) : IPCError
|
||||
|
||||
# connection to a service
|
||||
fun ipc_connection(LibC::Char**, Connection*, LibC::Char*) : IPCError
|
||||
# ipc_message_copy: pm, @fd, @mtype, @utype, @payload
|
||||
fun ipc_message_copy(Message*, LibC::Int, UInt8, UInt8, LibC::Char*, Int32)
|
||||
|
||||
fun ipc_read(Connection*, Message*) : IPCError
|
||||
fun ipc_write(Connection*, Message*) : IPCError
|
||||
# Closing connections.
|
||||
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
|
||||
fun ipc_del(Connections*, Connection*) : IPCError
|
||||
fun ipc_add_fd(Connections*, LibC::Int) : IPCError
|
||||
fun ipc_del_fd(Connections*, LibC::Int) : IPCError
|
||||
# Loop function.
|
||||
fun ipc_wait_event(Ctx*, Event*, 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
|
||||
fun ipc_connections_close(Connections*) # Void
|
||||
# Sending a message (will wait the fd to become available for IO operations).
|
||||
fun ipc_write(Ctx*, Message*) : IPCError
|
||||
|
||||
# Sending a message NOW.
|
||||
# WARNING: unbuffered send do not wait the fd to become available.
|
||||
fun ipc_write_fd(Int32, Message*) : IPCError
|
||||
|
||||
# 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.
|
||||
fun ipc_errors_get (LibC::Int) : LibC::Char*
|
||||
|
||||
|
||||
# networkd-related functions
|
||||
fun ipc_wait_event_networkd(Connections*, Connection*, Event*, Switchings*, LibC::Double*) : IPCError
|
||||
|
||||
# Exchanging file descriptors (used with ipcd on connection).
|
||||
fun ipc_receive_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*, Int16* -> LibIPC::IPCCB),
|
||||
(LibC::Int, LibIPC::Message* -> LibIPC::IPCCB))
|
||||
|
||||
fun ipc_ctx_switching_add (ctx : Ctx*, fd1 : LibC::Int, fd2 : LibC::Int) # Void
|
||||
fun ipc_ctx_switching_del (ctx : Ctx*, fd : LibC::Int) : LibC::Int
|
||||
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_get (switch : Switchings*, fd : LibC::Int ) : LibC::Int
|
||||
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
|
||||
fun ipc_read_fd(fd : Int32, message : Message*) : IPCError
|
||||
|
||||
# for testing purposes
|
||||
fun ipc_switching_print (switch : Switchings*) # Void
|
||||
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
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
require "./lowlevel"
|
||||
require "cbor"
|
||||
require "json"
|
||||
|
||||
# JSON is currently used for messages over websockets
|
||||
# At some point, this will be replaced by the CBOR format
|
||||
|
||||
class IPC::Message
|
||||
|
||||
property fd : Int32 # file descriptor
|
||||
property mtype : UInt8 # libipc message type
|
||||
property utype : UInt8 # libipc user message type
|
||||
property payload : Bytes
|
||||
|
||||
|
||||
# Clients send and receive JSON (or CBOR) payloads.
|
||||
struct JSONMessage
|
||||
include JSON::Serializable
|
||||
|
||||
|
@ -21,23 +23,46 @@ class IPC::Message
|
|||
end
|
||||
end
|
||||
|
||||
def self.from_json (str : String)
|
||||
def self.from_json (str : String) : IPC::Message
|
||||
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
|
||||
|
||||
def to_json
|
||||
def to_json : String
|
||||
JSONMessage.new(@utype, String.new(@payload), @mtype).to_json
|
||||
end
|
||||
|
||||
struct CBORMessage
|
||||
include CBOR::Serializable
|
||||
|
||||
property mtype : UInt8 = 1 # libipc message type
|
||||
property utype : UInt8 # libipc user message type
|
||||
property payload : Bytes
|
||||
|
||||
def initialize(@utype, @payload, @mtype = 1)
|
||||
end
|
||||
end
|
||||
|
||||
def self.from_cbor (m : Bytes) : IPC::Message
|
||||
cbor_message = CBORMessage.from_cbor m
|
||||
IPC::Message.new 0, cbor_message.mtype, cbor_message.utype, cbor_message.payload
|
||||
end
|
||||
|
||||
def to_cbor : Bytes
|
||||
CBORMessage.new(@utype, @payload, @mtype).to_cbor
|
||||
end
|
||||
|
||||
|
||||
|
||||
def initialize(message : Pointer(LibIPC::Message))
|
||||
if message.null?
|
||||
@mtype = LibIPC::MessageType::Error.to_u8
|
||||
@fd = 0
|
||||
@utype = 0
|
||||
@payload = Bytes.new "".to_unsafe, 0
|
||||
else
|
||||
m = message.value
|
||||
@fd = m.fd
|
||||
@mtype = m.type
|
||||
@utype = m.user_type
|
||||
@payload = Bytes.new m.payload, m.length
|
||||
|
@ -48,14 +73,12 @@ class IPC::Message
|
|||
initialize pointerof(message)
|
||||
end
|
||||
|
||||
def initialize(mtype, utype, payload : Bytes)
|
||||
def initialize(@fd, mtype, @utype, @payload : Bytes)
|
||||
@mtype = mtype.to_u8
|
||||
@utype = utype
|
||||
@payload = payload
|
||||
end
|
||||
|
||||
def initialize(mtype, utype, payload : String)
|
||||
initialize(mtype, utype, Bytes.new(payload.to_unsafe, payload.bytesize))
|
||||
def initialize(fd, mtype, utype, payload : String)
|
||||
initialize(fd, mtype, utype, Bytes.new(payload.to_unsafe, payload.bytesize))
|
||||
end
|
||||
|
||||
def self.to_packet (user_type : Int, message : String)
|
||||
|
@ -76,6 +99,10 @@ class IPC::Message
|
|||
IPC::Message.to_packet @utype, String.new(@payload)
|
||||
end
|
||||
|
||||
def copy_to_message_pointer(pm : LibIPC::Message*)
|
||||
LibIPC.ipc_message_copy pm, @fd, @mtype, @utype, @payload, @payload.size
|
||||
end
|
||||
|
||||
def to_s
|
||||
"(internal) utype #{@mtype}, (user) utype #{@utype}, payload #{String.new @payload}"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
# 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.
|
||||
# FIXME: for now, let's forget that.
|
||||
# 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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
require "./lowlevel"
|
||||
|
||||
class IPC::Switch
|
||||
@switch = LibIPC::Switchings.new
|
||||
|
|
28
src/json.cr
28
src/json.cr
|
@ -5,29 +5,41 @@ require "./ipc.cr"
|
|||
class IPC::JSON
|
||||
include ::JSON::Serializable
|
||||
|
||||
@[::JSON::Field(ignored: true)]
|
||||
getter type = -1
|
||||
class_getter type = -1
|
||||
#@[::JSON::Field(ignored: true)]
|
||||
class_property type = -1
|
||||
|
||||
property id : ::JSON::Any?
|
||||
|
||||
def type
|
||||
@@type
|
||||
end
|
||||
|
||||
macro message(id, type, &block)
|
||||
class {{id}} < ::IPC::JSON
|
||||
include ::JSON::Serializable
|
||||
|
||||
@@type = {{type}}
|
||||
def type
|
||||
@@type
|
||||
end
|
||||
|
||||
{{yield}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IPC::Connection
|
||||
class IPC::Context
|
||||
def send(fd : Int32, message : IPC::JSON)
|
||||
send fd, message.type.to_u8, message.to_json
|
||||
end
|
||||
def send_now(fd : Int32, message : IPC::JSON)
|
||||
send_now fd, message.type.to_u8, message.to_json
|
||||
end
|
||||
end
|
||||
|
||||
class IPC::Client
|
||||
def send(message : IPC::JSON)
|
||||
send message.type.to_u8, message.to_json
|
||||
send @server_fd.not_nil!, message.type.to_u8, message.to_json
|
||||
end
|
||||
def send_now(message : IPC::JSON)
|
||||
send_now @server_fd.not_nil!, message.type.to_u8, message.to_json
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
CRED = "\033[31m"
|
||||
CBLUE = "\033[36m"
|
||||
CGREEN = "\033[32m"
|
||||
CRESET = "\033[00m"
|
||||
CORANGE = "\033[33m"
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
# Context class, so the variables are available everywhere.
|
||||
class Context
|
||||
class_property requests = [] of IPC::CBOR.class
|
||||
class_property responses = [] of IPC::CBOR.class
|
||||
end
|
||||
|
||||
class IPC::CBOR
|
||||
def handle
|
||||
raise "unimplemented"
|
||||
end
|
||||
end
|
||||
|
||||
IPC::CBOR.message Message, 10 do
|
||||
property content : String?
|
||||
property some_number : Int32?
|
||||
def initialize(@content = nil, @some_number = nil)
|
||||
end
|
||||
|
||||
def handle
|
||||
info "message received: #{@content}, number: #{@some_number}"
|
||||
if number = @some_number
|
||||
::MessageReceived.new number - 1
|
||||
else
|
||||
::MessageReceived.new
|
||||
end
|
||||
end
|
||||
end
|
||||
Context.requests << Message
|
||||
|
||||
|
||||
IPC::CBOR.message Error, 0 do
|
||||
property reason : String
|
||||
def initialize(@reason)
|
||||
end
|
||||
end
|
||||
Context.responses << Error
|
||||
|
||||
IPC::CBOR.message MessageReceived, 20 do
|
||||
property minus_one : Int32?
|
||||
def initialize(@minus_one = nil)
|
||||
end
|
||||
|
||||
def handle
|
||||
info "<< MessageReceived (#{@minus_one})"
|
||||
end
|
||||
end
|
||||
Context.responses << MessageReceived
|
|
@ -0,0 +1,3 @@
|
|||
require "../src/ipc.cr"
|
||||
|
||||
IPC::Client.new "pong"
|
|
@ -0,0 +1,117 @@
|
|||
require "option_parser"
|
||||
require "../src/ipc.cr"
|
||||
require "../src/cbor.cr"
|
||||
require "./prints.cr"
|
||||
|
||||
require "cbor"
|
||||
require "./message"
|
||||
|
||||
class IPC::CBOR
|
||||
def handle
|
||||
raise "unimplemented"
|
||||
end
|
||||
end
|
||||
|
||||
class CLI
|
||||
class_property service_name = "pong"
|
||||
class_property message : String? = nil
|
||||
class_property type = 1
|
||||
class_property user_type = 42
|
||||
class_property verbosity = 1
|
||||
class_property rounds = 1
|
||||
end
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.on "-s service_name", "--service-name service_name", "URI" do |optsn|
|
||||
CLI.service_name = optsn
|
||||
end
|
||||
|
||||
parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0 = nothing is printed, 1 = only events, 2 = events and messages). Default: 1" do |optsn|
|
||||
CLI.verbosity = optsn.to_i
|
||||
end
|
||||
|
||||
parser.on "-t message_type",
|
||||
"--type message_type",
|
||||
"(internal) message type." do |opt|
|
||||
CLI.type = opt.to_i
|
||||
end
|
||||
|
||||
parser.on "-u user_message_type",
|
||||
"--user-type user_message_type",
|
||||
"Message type." do |opt|
|
||||
CLI.user_type = opt.to_i
|
||||
end
|
||||
|
||||
|
||||
parser.on "-r rounds", "--rounds count", "Number of messages sent." do |opt|
|
||||
CLI.rounds = opt.to_i
|
||||
end
|
||||
|
||||
parser.on "-m message", "--message m", "Message to sent." do |opt|
|
||||
CLI.message = opt
|
||||
end
|
||||
|
||||
parser.on "-h", "--help", "Show this help" do
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
client = IPC::Client.new CLI.service_name
|
||||
client.base_timer = 30_000 # 30 seconds
|
||||
client.timer = 30_000 # 30 seconds
|
||||
|
||||
server_fd = client.server_fd
|
||||
if server_fd.nil?
|
||||
puts "there is no server_fd!!"
|
||||
exit 1
|
||||
end
|
||||
|
||||
nb_messages_remaining = CLI.rounds
|
||||
|
||||
# Listening on STDIN.
|
||||
client << 0
|
||||
|
||||
client.loop do |event|
|
||||
case event
|
||||
when IPC::Event::ExtraSocket
|
||||
info "reading on #{event.fd}"
|
||||
|
||||
mstr = if CLI.message.nil?
|
||||
if event.fd == 0 STDIN.gets || "STDIN failed!" else "coucou" end
|
||||
else
|
||||
CLI.message.not_nil!
|
||||
end
|
||||
|
||||
m = Message.new mstr, nb_messages_remaining
|
||||
|
||||
info ">> Message"
|
||||
|
||||
CLI.rounds.times do |i|
|
||||
client.send server_fd.not_nil!, m
|
||||
end
|
||||
when IPC::Event::MessageReceived
|
||||
nb_messages_remaining -= 1
|
||||
response = Context.responses.parse_ipc_cbor event.message
|
||||
response.handle
|
||||
case response
|
||||
when MessageReceived
|
||||
info "#{nb_messages_remaining} messages remaining"
|
||||
if nb_messages_remaining == 0
|
||||
exit 0
|
||||
end
|
||||
when Error
|
||||
end
|
||||
when IPC::Event::Disconnection
|
||||
info "Disconnection from #{event.fd}"
|
||||
if event.fd == 0
|
||||
client.remove_fd 0
|
||||
end
|
||||
else
|
||||
info "unhandled event: #{event.class}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main
|
|
@ -0,0 +1,101 @@
|
|||
require "option_parser"
|
||||
require "../src/ipc.cr"
|
||||
require "./prints.cr"
|
||||
|
||||
class CLI
|
||||
class_property service_name = "pong"
|
||||
class_property message : String? = nil
|
||||
class_property type = 1
|
||||
class_property user_type = 42
|
||||
class_property verbosity = 1
|
||||
class_property rounds = 1
|
||||
end
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.on "-s service_name", "--service-name service_name", "URI" do |optsn|
|
||||
CLI.service_name = optsn
|
||||
end
|
||||
|
||||
parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0 = nothing is printed, 1 = only events, 2 = events and messages). Default: 1" do |optsn|
|
||||
CLI.verbosity = optsn.to_i
|
||||
end
|
||||
|
||||
parser.on "-t message_type",
|
||||
"--type message_type",
|
||||
"(internal) message type." do |opt|
|
||||
CLI.type = opt.to_i
|
||||
end
|
||||
|
||||
parser.on "-u user_message_type",
|
||||
"--user-type user_message_type",
|
||||
"Message type." do |opt|
|
||||
CLI.user_type = opt.to_i
|
||||
end
|
||||
|
||||
|
||||
parser.on "-r rounds", "--rounds count", "Number of messages sent." do |opt|
|
||||
CLI.rounds = opt.to_i
|
||||
end
|
||||
|
||||
parser.on "-m message", "--message m", "Message to sent." do |opt|
|
||||
CLI.message = opt
|
||||
end
|
||||
|
||||
parser.on "-h", "--help", "Show this help" do
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
client = IPC::Client.new CLI.service_name
|
||||
client.base_timer = 30_000 # 30 seconds
|
||||
client.timer = 30_000 # 30 seconds
|
||||
|
||||
server_fd = client.server_fd
|
||||
if server_fd.nil?
|
||||
puts "there is no server_fd!!"
|
||||
exit 1
|
||||
end
|
||||
|
||||
nb_messages_remaining = CLI.rounds
|
||||
|
||||
# Listening on STDIN.
|
||||
client << 0
|
||||
|
||||
client.loop do |event|
|
||||
case event
|
||||
when IPC::Event::ExtraSocket
|
||||
puts "extra socket fd #{event.fd}"
|
||||
info "reading on #{event.fd}"
|
||||
if event.fd == 0
|
||||
puts "reading on STDIN"
|
||||
end
|
||||
|
||||
mstr = if CLI.message.nil?
|
||||
if event.fd == 0 STDIN.gets || "STDIN failed!" else "coucou" end
|
||||
else
|
||||
CLI.message.not_nil!
|
||||
end
|
||||
|
||||
CLI.rounds.times do |i|
|
||||
client.send server_fd.not_nil!, CLI.user_type.to_u8, mstr.to_slice
|
||||
end
|
||||
when IPC::Event::MessageReceived
|
||||
nb_messages_remaining -= 1
|
||||
info "new message from #{event.fd}: #{event.message.to_s}, remaining #{nb_messages_remaining}"
|
||||
if nb_messages_remaining == 0
|
||||
exit 0
|
||||
end
|
||||
when IPC::Event::Disconnection
|
||||
info "Disconnection from #{event.fd}"
|
||||
if event.fd == 0
|
||||
client.remove_fd 0
|
||||
end
|
||||
else
|
||||
info "unhandled event: #{event.class}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main
|
|
@ -0,0 +1,96 @@
|
|||
require "option_parser"
|
||||
require "cbor"
|
||||
require "../src/ipc.cr"
|
||||
require "../src/cbor.cr"
|
||||
require "./prints.cr"
|
||||
|
||||
require "./message"
|
||||
|
||||
class CLI
|
||||
class_property service_name = "pong"
|
||||
class_property verbosity = 1
|
||||
class_property timer = 30_000
|
||||
class_property no_response = false
|
||||
end
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.on "-s service_name", "--service-name service_name", "URI" do |optsn|
|
||||
CLI.service_name = optsn
|
||||
end
|
||||
|
||||
parser.on "-n", "--no-response", "Do not provide any response back." do
|
||||
CLI.no_response = true
|
||||
end
|
||||
|
||||
parser.on "-t timer", "--timer ms", "Timer in ms. Default: 30 000" do |optsn|
|
||||
CLI.timer = optsn.to_i
|
||||
end
|
||||
|
||||
|
||||
parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0 = nothing is printed, 1 = only events, 2 = events and messages). Default: 1" do |optsn|
|
||||
CLI.verbosity = optsn.to_i
|
||||
end
|
||||
|
||||
parser.on "-h", "--help", "Show this help" do
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
service = IPC::Server.new CLI.service_name
|
||||
service.base_timer = CLI.timer # default: 30 seconds
|
||||
service.timer = CLI.timer # default: 30 seconds
|
||||
|
||||
service.loop do |event|
|
||||
# service.pp
|
||||
case event
|
||||
when IPC::Event::Timer
|
||||
info "IPC::Event::Timer"
|
||||
when IPC::Event::Connection
|
||||
info "IPC::Event::Connection, client: #{event.fd}"
|
||||
when IPC::Event::Disconnection
|
||||
info "IPC::Event::Disconnection, client: #{event.fd}"
|
||||
when IPC::Event::MessageSent
|
||||
begin
|
||||
info "IPC::Event::MessageSent, client: #{event.fd}"
|
||||
rescue e
|
||||
important "#{e.message}"
|
||||
service.remove_fd event.fd
|
||||
end
|
||||
|
||||
when IPC::Event::MessageReceived
|
||||
begin
|
||||
info "IPC::Event::MessageReceived, client: #{event.fd}"
|
||||
|
||||
request = Context.requests.parse_ipc_cbor event.message
|
||||
|
||||
if request.nil?
|
||||
raise "unknown request type"
|
||||
end
|
||||
|
||||
info "<< #{request.class.name}"
|
||||
|
||||
response = begin
|
||||
request.handle
|
||||
rescue e
|
||||
important "#{request.class.name} generic error #{e}"
|
||||
::Error.new "unknown error"
|
||||
end
|
||||
|
||||
unless CLI.no_response
|
||||
info ">> #{response.class.name}"
|
||||
service.send event.fd, response.not_nil!
|
||||
end
|
||||
|
||||
rescue e
|
||||
important "#{e.message}"
|
||||
service.remove_fd event.fd
|
||||
end
|
||||
else
|
||||
important "Exception: message #{event}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main
|
|
@ -0,0 +1,78 @@
|
|||
require "option_parser"
|
||||
require "../src/ipc.cr"
|
||||
require "./prints.cr"
|
||||
|
||||
class CLI
|
||||
class_property service_name = "pong"
|
||||
class_property verbosity = 1
|
||||
class_property timer = 30_000
|
||||
class_property no_response = false
|
||||
end
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.on "-s service_name", "--service-name service_name", "URI" do |optsn|
|
||||
CLI.service_name = optsn
|
||||
end
|
||||
|
||||
parser.on "-n", "--no-response", "Do not provide any response back." do
|
||||
CLI.no_response = true
|
||||
end
|
||||
|
||||
parser.on "-t timer", "--timer ms", "Timer in ms. Default: 30 000" do |optsn|
|
||||
CLI.timer = optsn.to_i
|
||||
end
|
||||
|
||||
|
||||
parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0 = nothing is printed, 1 = only events, 2 = events and messages). Default: 1" do |optsn|
|
||||
CLI.verbosity = optsn.to_i
|
||||
end
|
||||
|
||||
parser.on "-h", "--help", "Show this help" do
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
service = IPC::Server.new CLI.service_name
|
||||
service.base_timer = CLI.timer # default: 30 seconds
|
||||
service.timer = CLI.timer # default: 30 seconds
|
||||
|
||||
service.loop do |event|
|
||||
# service.pp
|
||||
case event
|
||||
when IPC::Event::Timer
|
||||
info "IPC::Event::Timer"
|
||||
when IPC::Event::Connection
|
||||
info "IPC::Event::Connection, client: #{event.fd}"
|
||||
when IPC::Event::Disconnection
|
||||
info "IPC::Event::Disconnection, client: #{event.fd}"
|
||||
when IPC::Event::MessageSent
|
||||
begin
|
||||
info "IPC::Event::MessageSent, client: #{event.fd}"
|
||||
rescue e
|
||||
important "#{e.message}"
|
||||
service.remove_fd event.fd
|
||||
end
|
||||
when IPC::Event::MessageReceived
|
||||
begin
|
||||
info "IPC::Event::MessageReceived, client: #{event.fd}"
|
||||
m = String.new event.message.payload
|
||||
debug "message type #{event.message.utype}: #{m}"
|
||||
|
||||
unless CLI.no_response
|
||||
service.send event.message
|
||||
debug "sending message..."
|
||||
end
|
||||
|
||||
rescue e
|
||||
important "#{e.message}"
|
||||
service.remove_fd event.fd
|
||||
end
|
||||
else
|
||||
important "Exception: message #{event}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main
|
|
@ -0,0 +1,13 @@
|
|||
require "./colors"
|
||||
|
||||
def important(message : String)
|
||||
puts "#{CRED}#{message}#{CRESET}" if CLI.verbosity > 0
|
||||
end
|
||||
|
||||
def info(message : String)
|
||||
puts "#{CGREEN}#{message}#{CRESET}" if CLI.verbosity > 1
|
||||
end
|
||||
|
||||
def debug(message : String)
|
||||
puts "#{CBLUE}#{message}#{CRESET}" if CLI.verbosity > 2
|
||||
end
|
Loading…
Reference in New Issue