#!/opt/bin/perl =head1 NAME aemp - AnyEvent:MP utility =head1 SYNOPSIS aemp command args... # protocol commands aemp snd # send a message aemp mon # wait till port is killed aemp rpc # send message, append reply # run a node aemp run initialise_args... # run a node # node configuration: protocol endpoints aemp setnoderef # configure the real noderef aemp clrnoderef # reset noderef to default # node configuration: secret aemp gensecret # generate a random shared secret aemp setsecret # set the shared secret aemp clrsecret # remove the secret # node configuration: TLS aemp setcert # set a certificate (key.pem + certificate.pem) aemp clrcert # remove certificate aemp gencert # generate a random certificate # node configuration: seed nodes for bootstrapping aemp setseeds ... # set seednodes aemp addseed # add a seednode aemp delseed # remove seednode # node configuration: services aemp setservices initfunc... # set service functions aemp addservice # add an instance of a service aemp delservice # delete one instance of a service # profile-specific configuration aemp profile ... # apply command to profile only aemp delprofile # eradicate the named profile # debugging aemp trace # trace the network topology =head1 DESCRIPTION With aemp you can configure various aspects of AnyEvent::MP and its protocol. You can also start a "default node", a node that only depends on the static configuration. =cut use common::sense; # must come before anything else BEGIN { if ($ARGV[0] eq "run") { shift; # d'oh require AnyEvent::Watchdog; AnyEvent::Watchdog::autorestart (1); AnyEvent::Watchdog::heartbeat (300); require AnyEvent; require AnyEvent::MP; AnyEvent::MP::initialise_node (@ARGV); AnyEvent::detect () eq "AnyEvent::Impl::E" ? EV::loop () : AE::cv ()->recv; } } use Carp (); use AnyEvent; use AnyEvent::Util; use AnyEvent::MP; use AnyEvent::MP::Config; sub my_run_cmd { my ($cmd) = @_; my $cv = &run_cmd; my $status = $cv->recv; $status and die "@$cmd: command failed with exit status $status."; } sub gen_cert { my_run_cmd [qw(openssl req -new -nodes -x509 -days 3650 -newkey rsa:2048 -keyout /dev/fd/3 -batch -subj /CN=AnyEvent::MP )], "<", "/dev/null", ">" , \my $cert, "3>", \my $key, "2>", "/dev/null"; "$cert$key" } our $cfg = AnyEvent::MP::Config::config; our $profile = $cfg; sub resolve_port { my ($node, $port) = split /#/, $_[0], 2; $node = (resolve_node $node)->recv; "$node#$port" } sub trace { my ($node) = @_; my $cv = AE::cv; my %seen; my $to = AE::timer 15, 0, sub { warn "timeout\n"; $cv->(); }; initialise_node "slave/", $node; my $reply = port { my ($node, @neigh) = @_; @neigh = grep $_ ne $NODE, @neigh; print +(join " ", $node, @neigh), "\n"; for (@neigh) { unless ($seen{$_}++) { $cv->begin; snd $_, up_nodes => $SELF => $_; } } $cv->end; }; $cv->begin; snd $reply, seed => $node; $cv->recv; } sub docmd; our %CMD = ( snd => sub { my $port = resolve_port shift @ARGV; initialise_node "slave/", node_of $port; snd $port, @ARGV; @ARGV = (); my $cv = AE::cv; my $to = AE::timer 5, 0, sub { $cv->("timeout") }; mon $port, $cv; my $reply = port { &$cv; 1 }; snd node_of $port, snd => $reply, "ok"; print join " ", $cv->recv, "\n"; }, rpc => sub { my $port = resolve_port shift @ARGV; initialise_node "slave/", node_of $port; my $cv = AE::cv; my $to = AE::timer 5, 0, sub { $cv->("timeout") }; my $reply = port { &$cv; 1 }; snd $port, @ARGV, $reply; @ARGV = (); mon $port, $cv; print join " ", $cv->recv, "\n"; }, mon => sub { my $port = resolve_port shift @ARGV; initialise_node "slave/", node_of $port; mon $port, my $cv = AE::cv; print join " ", $cv->recv, "\n"; }, trace => sub { @ARGV >= 1 or die "noderef missing\n"; trace +(resolve_node shift @ARGV)->recv; }, setnoderef => sub { @ARGV >= 1 or die "shared secret missing\n"; $profile->{noderef} = shift @ARGV; ++$cfg->{dirty}; }, clrnoderef => sub { delete $profile->{noderef}; ++$cfg->{dirty}; }, setsecret => sub { @ARGV >= 1 or die "shared secret missing\n"; $profile->{secret} = shift @ARGV; ++$cfg->{dirty}; }, gensecret => sub { $profile->{secret} = AnyEvent::MP::Base::asciibits AnyEvent::MP::Base::nonce 64; ++$cfg->{dirty}; }, clrsecret => sub { delete $profile->{secret}; ++$cfg->{dirty}; }, setcert => sub { @ARGV >= 1 or die "key+certificate pem filename missing\n"; open my $fh, "<", $ARGV[0] or die "$ARGV[0]: $!"; local $/; $profile->{cert} = <$fh>; ++$cfg->{dirty}; }, gencert => sub { $profile->{cert} = gen_cert; ++$cfg->{dirty}; }, clrcert => sub { delete $profile->{cert}; ++$cfg->{dirty}; }, setseeds => sub { $profile->{seeds} = [@ARGV]; @ARGV = (); ++$cfg->{dirty}; }, addseed => sub { @ARGV >= 1 or die "seed noderef missing\n"; my $seed = shift @ARGV; @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} }; push @{ $profile->{seeds} }, $seed; ++$cfg->{dirty}; }, delseed => sub { @ARGV >= 1 or die "seed noderef missing\n"; my $seed = shift @ARGV; @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} }; ++$cfg->{dirty}; }, setservices => sub { $profile->{services} = [@ARGV]; @ARGV = (); ++$cfg->{dirty}; }, addservice => sub { @ARGV >= 1 or die "service specification missing\n"; my $service = shift @ARGV; push @{ $profile->{services} }, $service; ++$cfg->{dirty}; }, delservice => sub { @ARGV >= 1 or die "service specification missing\n"; my $service = shift @ARGV; for (0 .. $#{ $profile->{services} }) { next unless $profile->{services}[$_] eq $service; splice @{ $profile->{services} }, $_, 1; last; } ++$cfg->{dirty}; }, profile => sub { @ARGV >= 2 or die "profile name or subcommand are missing\n"; my $name = shift @ARGV; $profile = $cfg->{profile}{$name} ||= {}; ++$cfg->{dirty}; docmd; }, delprofile => sub { @ARGV >= 1 or die "profile name is missing\n"; my $name = shift @ARGV; delete $cfg->{profile}{$name}; ++$cfg->{dirty}; }, ); sub docmd { my $cmd = shift @ARGV; $CMD{$cmd} or die "$cmd: no such aemp command (try man aemp)"; $CMD{$cmd}(); } @ARGV or die "Usage: aemp subcommand ... (try man aemp)\n"; docmd;