4.0.0alpha.svn.7719
Amanda::IPC modules list
Amanda modules list
List of All Modules
All Amanda Releases

Amanda::IPC::LineProtocol

NAME

Amanda::IPC::LineProtocol -- parent class for line-based protocols

SYNOPSIS

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);

DESCRIPTION

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.

DEFINING A PROTOCOL

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 ) ] },
  );

USING A PROTOCOL

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");

ABOUT THIS PAGE

This page was automatically generated Tue Mar 19 07:08:16 2019 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.


4.0.0alpha.svn.7719