App-Diskd

 view release on metacpan or  search on metacpan

lib/App/Diskd.pm  view on Meta::CPAN


use POE;

sub Daemon {

  print "Starting diskd in daemon mode\n";

  my $info = Local::Info->new;

  my $blkid_session = Local::DiskWatcher->new(info => $info,);
  my $usock_session = Local::UnixSocketServer->new(info => $info);
  my $multi_session = Local::MulticastServer->new(info => $info, ttl=>2);

  POE::Kernel->run();

}

sub Client {

  print "Starting diskd in client mode\n";

  my $usock_client = Local::UnixSocketClient->new;

  POE::Kernel->run();
}

1;

##
## The Info package is intended to provide a central area where we can
## store details of known disks and hosts. It just provides some
## useful get/set interfaces that the other packages can use.

lib/App/Diskd.pm  view on Meta::CPAN


  my $class = shift;
  my %opts = (
	      initial_delay => 5,
	      frequency => 10 * 60,
	      info => undef,
	      ttl => 1,		# set >1 to traverse routers
	      @_
	     );

  die "UnixSocketServer::new requires info => \$var option\n"
    unless defined($opts{info});

  my $session =
    POE::Session->create(
	inline_states => {
	       	   _start         => \&peer_start,
	       	   get_datagram   => \&peer_read,
	       	   send_something => \&send_something,
	       	  },
	heap => {

lib/App/Diskd.pm  view on Meta::CPAN

  #  my $message = "pid $$ sending at " . time() . " to " . MCAST_DESTINATION;
  my $message = "diskd:" . $info->pack_our_disk_list;

  warn $! unless $socket->mcast_send($message, MCAST_DESTINATION);

  $kernel->delay(send_something => $delay);
}


##
## The UnixSocketServer package uses a Unix domain socket to provide a
## local ineterface to the disk info and a means of sending commands
## or messages to other nodes in our multicast network.
##
## This package comprises a main server package (UnixSocketServer)
## that waits for connections to the socket, and and a package that's
## spawned for each incoming connection (UnixSocketServer::Session).
##

package Local::UnixSocketServer;

use POE qw(Wheel::SocketFactory Wheel::ReadWrite);
use Socket;          # For PF_UNIX.

# Start server at a particular rendezvous (ie, Unix domain socket)
sub new {
  my $class   = shift;
  my $homedir = $ENV{HOME};
  my %opts    =
    (
     rendezvous => "$homedir/.diskd-socket",
     info       => undef,
     @_,
    );

  # warn "class: $class; opts: " . (join ", ", @_);

  die "UnixSocketServer::new requires info => \$var option\n"
    unless defined($opts{info});

  POE::Session->create(
    inline_states => {
      _start     => \&server_started,
      got_client => \&server_accepted,
      got_error  => \&server_error,
    },
    heap => {
      rendezvous => $opts{rendezvous},

lib/App/Diskd.pm  view on Meta::CPAN

  $error = "Normal client disconnection." unless $errno;
  warn "Server socket encountered $syscall error $errno: $error\n";
  delete $heap->{server};
}

# The server accepted a connection.  Start another session to process
# data on it.
sub server_accepted {
  my ($heap,$client_socket) = @_[HEAP, ARG0];
  my $info = $heap->{info};
  Local::UnixSocketServer::Session->new($client_socket, $info);
}

## A UnixSocketServer::Session instance is created for each incoming
## connection.

package Local::UnixSocketServer::Session;

use POE::Session;

# Constructor
sub new {
  my ($class,$socket,$info) = @_;
  #warn "new $class: $socket, $info";
  POE::Session->create(
    package_states => [ $class => [qw( _start session_input session_error)] ],
    args => [$info, $socket],

lib/App/Diskd.pm  view on Meta::CPAN

# the error and shut down this session.  The main server remains
# untouched by this.
sub session_error {
  my ($heap, $syscall, $errno, $error) = @_[HEAP, ARG0 .. ARG2];
  $error = "Normal disconnection." unless $errno;
  warn "Server session encountered $syscall error $errno: $error\n";
  delete $heap->{client};
}


package Local::UnixSocketClient;

# This program is a simple unix socket client.  It will connect to the
# UNIX socket specified by $rendezvous.  This program is written to
# work with the UnixServer example in POE's cookbook.  While it
# touches upon several POE modules, it is not meant to be an
# exhaustive example of them.  Please consult "perldoc [module]" for
# more details.

use Socket qw(AF_UNIX);
use POE;                          # For base features.
use POE::Wheel::SocketFactory;    # To create sockets.
use POE::Wheel::ReadWrite;        # To read/write lines with sockets.
use POE::Wheel::ReadLine;         # To read/write lines on the console.

lib/App/Diskd.pm  view on Meta::CPAN

  => <EOF>			# ^D exits client

=head1 DESCRIPTION

This program is intended as an example of:

=over

=item 1. using multicast to send and receive data among several peers

=item 2. communicating with local clients via a Unix domain socket

=item 3. using POE to achieve both of the above

=item 4. using POE to periodically run an external program without blocking

=item 5. encapsulating a data structure that can be accessed and updated by the above

=back

The information shared between peers in this example is the list of

lib/App/Diskd.pm  view on Meta::CPAN

machine to other peers that have joined the multicast channel. It also
listens to the channel for incoming multicast messages from another
peer and uses them to update its list of which disks are attached to
that peer. As a result of this, each daemon will be able to build up a
full list of which disks are available in the peer network and to
which machine they are attached. Thus the primary function of the
program is to be able to locate disks, no matter which machine they
are currently attached to.

The diskd program can also be run in client mode on any machine that
has a running diskd daemon. The client conencts via a local unix
domain socket and, providing the connection succeeds, it will then be
able to pass commands to the daemon. Currently the only useful command
that is implemented is 'list', which prints a list of all the disks
that the daemon knows about. More commands could be added quite
easily.

=head1 MOTIVATION/GENESIS

The reason for writing this program was to explore three key areas:

lib/App/Diskd.pm  view on Meta::CPAN

The MulticastServer object relies on the Info object to provide
(de-)serialisation of the data. The way this is currently implemented
(using YAML and some extra checking on the received data structure),
this prevents the possibility of a rogue peer joining the network and
sending data packets that are specially crafted so as to allow them to
execute arbitrary Perl code (ie, receiving arbitrary data should not
present a security risk). The question of whether I<broadcasting>
(multicasting) information about what disks are attached represents a
security risk is left to the user to decide.

=head2 UnixSocketServer and UnixSocketServer::Session classes

Using standard OO terminology, the UnixSocketServer class is a
"Factory" that creates UnixSocketServer::Session objects. The
"Factory" class listens for new connections on a private Unix-domain
socket (basically, a file in the user's home directory that only that
user can access, which acts like a local socket). When a new
connection comes in, it creates a new UnixSocketServer::Session
object. Multiple connections can be created, with a new Session object
created for each one.

Once it is up and running, a UnixSocketServer::Session object then
reponds to commands like "help", "list" and so on that come through
the socket.

A simple enough extension of the current program would be implement a
command (in UnixSocketServer::Session) that causes the daemon to
multicast the current list of locally-attached disks to all peers,
regardless of the current timeout value. Similar commands could cause
the daemon to trigger the DiskWatcher or MountWatcher classes to
refresh their data.

A slightly more complicated extension would be a "ping"-like
command. The Session object would recognise it and then send out a
message to all peers requesting that they send their list of local
disks again. In order to prevent this from being abused (eg, a rogue
peer on the network using it to flood the network with traffic and
cause a Denial of Service attack), you might want to implement some
form of rate limiting in the MulticastServer class: basically, it
would limit the number of "ping" requests it would send answers to, so
that any excess ping requests in a given time period would be ignored.

=head2 UnixSocketClient class

This class is the counterpart to the UnixSocketServer and
UnixSocketServer::Session classes. It takes commands typed in by the
user, sends them to the server and displays the output.

This client incorporates ReadLine support (for editing of command
lines, as well as a history buffer) and graceful shutdown (on the
client side at least---the server side must close the Session down
based on seeing that the client side has closed the socket
connection).

It should be noted that this class is, strictly speaking, not
necessary. By passing the correct parameters to the "telnet" program,



( run in 1.264 second using v1.01-cache-2.11-cpan-64ef6c95b5d )