Define your protocol:
packge MyProtocol; use Amanda::IPC::LineProtocol; use base "Amanda::IPC::LineProtocol";
use constant SETSTATUS => message("SETSTATUS", match => qr/^FOO$/i, format => [ qw( param1 param2 optional? list* ) ], ); use constant PING => message("PING", match => qr/^PING$/i, format => [ qw( id ) ], ); use constant PONG => message("PONG", match => qr/^PONG$/i, format => [ qw( id ) ], ); # ...
# And use the protocol package main; my $input_fh = IO::Handle->new(...); my $output_fh = IO::Handle->new(...); my $proto;
my $ping_cb = make_cb(ping_cb => sub { my ($msg, %args) = @_; $proto->send(MyProtocol::PONG, id => $args{'id'}); });
my $message_cb = make_cb(message_cb => sub { my ($msg, %args) = @_; if (!$msg) { die $args{'error'}; } });
$proto = MyProtocol->new( rx_fh => $input_fh, tx_fh => $output_fh, message_cb => $message_cb);
# and add callbacks $proto->set_message_cb(MyProtocol::PING, $ping_cb); $proto->set_message_cb(MyProtocol::PONG, $pong_cb);
# or send messages to an object, with method names based on # the message name sub msg_PONG { my $self = shift; my ($msg, %args) = @_; } # .. $proto = MyProtocol->new( # .. message_obj => $self);
# send a message $proto->send(MyProtocol::SETSTATUS, param1 => "x", param2 => "y", );
# shut down the protocol, flushing any messages waiting to # be sent first my $finished_cb = make_cb(finished_cb => sub { my ($err) = @_; # ... }); $proto->stop(finished_cb => $finished_cb);
This library is used to implement communications between Amanda processes. Amanda has historically implemented a number of distinct text-based protocols for communications between various components, and this library servces to abstract and centralize the implementation of those protocols.
The package supports point-to-point, message-based, symmetric protocols. Two communicating processes exchange discrete messages, and in principle either process can send a message at any time, although this is limited by the (often unwritten) rules of the protocol.
In protocols based on this package, each message is a single text line, terminated with a newline and consisting of a sequence of quoted strings. The first string determines the type of message. For example:
SEND-MORE-MONEY $150.00 "Books and pencils" ORDER-PIZZA Large Mushrooms Olives Onions "Green Peppers"
The package is asynchronous (see Amanda::MainLoop), triggering callbacks for
incoming messages rather than implementing a get_message
method or the like.
If necessary, outgoing messages are queued for later transmission, thus
avoiding deadlocks from full pipe buffers. This allows processing to continue
unhindered in both processes while messages are in transit in either direction.
There are two parts to any use of this package. First, define the protocol by
creating a subclass and populating it using the message
package method.
This begins with something like
package CollegeProtocol; use base "Amanda::IPC::LineProtocol"; use Amanda::IPC::LineProtocol;
The usual trick for specifying messages is to simultaneously define a series of constants, using the following idiom:
use constant ORDER_PIZZA => message("ORDER-PIZZA", match => qr/^ORDER-PIZZA$/, format => [ qw( size toppings* ) ], );
The first argument to message
is the word with which this particular message
type will be sent. The match
parameter gives a regular expression which
will be used to recognize incoming messages of this type. If this parameter
is not specified, the default is to match the first argument with a
case-insensitive regexp.
The format
parameter describes the format of the arguments for this message
type. A format parameter with the *
suffix gathers all remaining arguments
into a list. A ?
suffix indicates an optional parameter. Note that it is
quite possible to specify ambiguous formats which will not work like you
expect. The default format is an empty list (taking no arguments).
The optional on_eof
parameter will cause a a message of this type to be
generated on EOF. For example, with:
use constant DROP_OUT => message("DROP-OUT", on_eof => 1, );
when an EOF is detected, a DROP_OUT
message will be generated.
The protocol class should contain, in POD, a full description of the syntax of the protcol -- which messages may be sent when, and what they mean. No facility is provided to encode this description in perl.
In general, protocols are expected to be symmetrical -- any message can either
be sent or received. However, some existing protocols use different formats in
different directions. In this case, specify format
as a hashref with keys
in
and out
pointing to the two different formats:
use constant ERROR => message("ERROR", match => qr/^ERROR$/, format => { in => [ qw( message severity ) ], out => [ qw( component severity message ) ] }, );
Once a protocol is defined, it forms a class which can be used to run the protocol. Multiple instances of this class can be created to handle simultaneous uses of the protocol over different channels.
The constructor, new
, takes two IO::Handle
objects -- one to read from
(rx_fh
) and one to write to (tx_fh
). In some cases (e.g., a socket),
these may be the same handle. It takes an optional callback, message_cb
,
which will be called for any received messages not handled by a more specific
callback. Any other parameters are considered message-type-specific callbacks.
For example, given a socket handle $sockh
, the following will start the
CollegeProtocol
running on that socket:
my $proto = CollegeProtocol->new( rx_fh => $sockh, tx_fh => $sockh, ); $proto->set_message_cb(CollegeProtocol::PIZZA_DELIVERY, $pizza_delivery_cb);
For protocols with a lot of message types, it may be useful to have the
protocol call methods on an object. This is done with the message_obj
argument to the protocol constructor:
$proto = CollegeProtocol->new( # .. message_obj => $obj);
The methods are named msg_$msgname
, where $msgname has all non-identifier
characters translated to an underscore (_
). For situations where the meaning
of a message can change dynamically, it may be useful to set a callback after
the object has been crated:
$proto->set_message_cb(CollegeProtocol::MIDTERM, sub { ... });
The constructor also takes a 'debug' argument; if given, then all incoming and outgoing messages will be written to the debug log with this argument as prefix.
All message callbacks have the same signature:
my $pizza_delivery_cb = make_cb(pizza_delivery_cb => sub { # (note that object methods will get the usual $self) my ($msgtype, %params) = @_; });
where %params
contains all of the arguments to the message, keyed by the
argument names given in the message's format
. Note that parameters
specified with the *
suffix will appear as arrayrefs.
Callbacks specified with set_message_cb
take precedence over other
specifications; next are message-specific callbacks given to the constructor,
followed by message_obj
, and finally message_cb
.
In case of an error, the message_cb
(if specified) is called with
$msgtype
undefined and with a single parameter named error
giving the
error message. This generally indicates either an unknown or badly-formatted
message.
To send a message, use the send
method, which takes the same arguments as a
message callback:
$proto->send(CollegeProtocol::SEND_MORE_MONEY, how_much => "$150.00", what_for => "Books and pencils");
This page was automatically generated Tue Oct 4 19:45:36 2016 from the Amanda source tree, and documents the most recent development version of Amanda. For documentation specific to the version of Amanda on your system, use the 'perldoc' command.