#!/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 # 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 aemp forcetls <0|1> # enforce TLS connections? =head1 DESCRIPTION With aemp you can configure various aspects of AnyEvent::MP and it's protocol. =cut use common::sense; use Carp (); use AnyEvent; use AnyEvent::MP::Config; use AnyEvent::MP; sub run_cmd { my $cmd = shift; require POSIX; local $^F = 1023; #d# my $cv = AE::cv; my %redir; my @exe; while (@_) { my $type = shift; my $fd = $type =~ s/^(\d+)// ? $1 : undef; # fd> if ($type eq ">") { my ($pr, $pw) = AnyEvent::Util::portable_pipe; my $cb = shift; $cv->begin; my $w; $w = AE::io $pr, 0, "SCALAR" eq ref $cb ? sub { sysread $pr, $$cb, 8192, length $$cb and return; undef $w; $cv->end; } : sub { my $buf; sysread $pr, $buf, 8192 and return $cb->($buf); undef $w; $cv->end; } ; $redir{defined $fd ? $fd : 1} = $pw; } elsif ($type =~ s/^>//) { push @exe, sub { open my $fh, ">", $type or POSIX::_exit (125); $redir{defined $fd ? $fd : 1} = $fh; }; } elsif ($type =~ s/^() for @exe; # step 2, move any existing fd's out of the way my @oldfh; for my $fh (values %redir) { push @oldfh, $fh; $fh = fileno $fh; defined ($fh = POSIX::dup ($fh)) or POSIX::_exit (124) while exists $redir{$fh}; } # step 3, execute redirects while (my ($k, $v) = each %redir) { defined POSIX::dup2 ($v, $k) or POSIX::_exit (123); } # step 4, close everything else for (3..1023) { #TODO POSIX::close ($_) unless exists $redir{$_}; } exec @$cmd; POSIX::_exit (126); } close $_ for values %redir; my $status; $cv->begin (sub { shift->send ($status) }); my $cw; $cw = AE::child $pid, sub { $status = $_[1]; undef $cw; $cv->end; }; $cv } 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 -batch -newkey rsa:2048 -keyout /dev/fd/3)], "" , \my $cert, "3>", \my $key, "2>/dev/null"; "$cert$key" } our $cfg = \%AnyEvent::MP::Config::CFG; our $nodecfg = $cfg->{node}{"#default"} ||= {}; sub resolve_port { my ($node, $port) = split /#/, $_[0], 2; $node = (resolve_node $node)->recv; "$node#$port" } our %CMD = ( snd => sub { my $port = resolve_port shift @ARGV; initialise_node "slave/", node_of $port; snd $port, @ARGV; my $cv = AE::cv; mon $port, $cv; my $reply = port { &$cv }; snd node_of $port, relay => $reply, "ok"; 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"; }, setsecret => sub { @ARGV == 1 or die "shared secret missing\n"; $nodecfg->{secret} = shift @ARGV; ++$cfg->{dirty}; }, gensecret => sub { $nodecfg->{secret} = AnyEvent::MP::Base::asciibits AnyEvent::MP::Base::nonce 64; ++$cfg->{dirty}; }, clrsecret => sub { delete $nodecfg->{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 $/; $nodecfg->{cert} = <$fh>; ++$cfg->{dirty}; }, gencert => sub { $nodecfg->{cert} = gen_cert; ++$cfg->{dirty}; }, clrcert => sub { delete $nodecfg->{cert}; ++$cfg->{dirty}; }, forcetls => sub { @ARGV == 1 or die "enable value missing\n"; $nodecfg->{forcetls} = !!shift @ARGV; ++$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;