ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.32
Committed: Thu Sep 3 07:57:31 2009 UTC (14 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.31: +5 -5 lines
Log Message:
fixed some typos in documentation.

File Contents

# Content
1 #!/OPT/BIn/perl
2
3 =head1 NAME
4
5 aemp - AnyEvent:MP utility
6
7 =head1 SYNOPSIS
8
9 aemp command args...
10
11 # protocol commands
12 aemp snd <port> <arg...> # send a message
13 aemp mon <port> # wait till port is killed
14 aemp rpc <port> <arg...> # send message, append reply
15 aemp eval <node> <expr...> # evaluate expression
16 aemp trace <nodeid> # trace the network topology
17
18 # run a node
19 aemp run configure_args... # run a node
20
21 # node configuration: node ID
22 aemp setnodeid <nodeid> # configure the real node id
23 aemp delnodeid # reset node id to default (= inherit)
24
25 # node configuration: secret
26 aemp gensecret # generate a random shared secret
27 aemp setsecret <secret> # set the shared secret
28 aemp delsecret # remove the secret (= inherit)
29
30 # node configuration: TLS
31 aemp gencert # generate a random certificate
32 aemp setcert <file> # set a certificate (key.pem + certificate.pem)
33 aemp delcert # remove certificate (= inherit)
34
35 # node configuration: seed addresses for bootstrapping
36 aemp setseeds <host:port>,... # set seeds
37 aemp delseeds # clear all seeds (= inherit)
38 aemp addseed <host:port> # add a seed
39 aemp delseed <host:port> # remove seed
40
41 # node configuration: bind addresses
42 aemp setbinds <host:port>,... # set binds
43 aemp delbinds # clear all binds (= inherit)
44 aemp addbind <host:port> # add a bind address
45 aemp delbind <host:port> # remove a bind address
46
47 # node configuration: services
48 aemp setservices initfunc,... # set service functions
49 aemp delservices # clear all services (= inherit)
50 aemp addservice <initfunc> # add an instance of a service
51 aemp delservice <initfunc> # delete one instance of a service
52
53 # profile-specific configuration
54 aemp profile <name> <command>... # apply command to profile only
55 aemp setparent <name> # specify a parent profile
56 aemp delparent # clear parent again
57 aemp delprofile <name> # eradicate the named profile
58 aemp showprofile <name> # display given profile
59 aemp showconfig <name> ... # display effective config
60
61 =head1 DESCRIPTION
62
63 With aemp you can configure various aspects of AnyEvent::MP and its
64 protocol, send various messages and even run a node.
65
66 The F<aemp> utility works like F<cvs>, F<svn> or other commands: the first
67 argument defines which operation (subcommand) is requested, after which
68 arguments for this operation are expected. When a subcommand does not eat
69 all remaining arguments, the remaining arguments will again be interpreted
70 as subcommand and so on.
71
72 This means you can chain multiple commands, which is handy for profile
73 configuration, e.g.:
74
75 aemp gensecret profile xyzzy binds 4040,4041 nodeid anon/
76
77 Please note that all C<setxxx> subcommands have an alias without the
78 C<set> prefix.
79
80 =head2 RUNNING A NODE
81
82 This can be used to run a node - together with some services, this makes
83 it unnecessary to write any wrapper programs.
84
85 =over 4
86
87 =item run <configure_args>...
88
89 Runs a node by calling C<AnyEvent::MP::Kernel::configure> with the given
90 arguments. The node runs under L<AnyEvent::Watchdog>, can be restarted
91 (and autorestarted, see the L<AnyEvent::Watchdog> manual). A very common
92 invocation is to just specify a profile using the profile parameter
93
94 aemp run profile database-backend
95
96 ... but you can use most arguments that C<configure> understands:
97
98 aemp run nodeid mynode2 profile someprofile
99
100 Care has been taken to load (almost) no modules other than
101 L<AnyEvent::Watchdog> and the modules it loads, so everything (including
102 the L<AnyEvent::MP> modules themselves) will be freshly loaded on restart,
103 which makes upgrading everything except the perl binary easy.
104
105 =back
106
107 =head2 PROTOCOL COMMANDS
108
109 These commands actually communicate with other nodes. They all use a node
110 profile name of C<aemp> (specifying a default node ID of C<anon/> and a
111 binds list containing C<*:*> only).
112
113 They all use a timeout of five seconds, after which they give up.
114
115 =over 4
116
117 =item snd <port> <arguments...>
118
119 Simply send a message to the given port - where you get the port ID from
120 is your problem.
121
122 Exits after ensuring that the message has been delivered to its node.
123
124 Most useful to take advantage of some undocumented functionality inside
125 nodes, such as node ports being able to call any method:
126
127 aemp snd doomed AnyEvent::Watchdog::restart 1
128
129 =item rpc <port> <arg...>
130
131 Like F<aemp snd>, but appends a local reply port to the message and waits
132 for a message to it.
133
134 Any return values will be JSON-encoded and printed separated by commas
135 (kind of like a JSON array without []-brackets).
136
137 Example: ask the (undocumented) time service of a node for it's current
138 time.
139
140 aemp rpc mynode time
141
142 =item mon <port>
143
144 Monitors the port and exits when it's monitorign callback is called. Most
145 useful to monitor node ports.
146
147 Example: monitor some node.
148
149 aemp mon doomed
150
151 =item eval <node> <expr...>
152
153 Joins all remaining arguments into a string and evaluates it on the given
154 node. Return values are handled as with F<aemp rpc>.
155
156 Example: find the unix process ID of the node called posicks.
157
158 aemp eval posicks '$$'
159
160 =item trace <node>
161
162 Asks the given node for all currently connected nodes, then asks those
163 nodes for the same, thus tracing all node connections.
164
165 =back
166
167 =head2 CONFIGURATION/NODE ID/SECRET/CERTIFICATE
168
169 These commands deal with rather basic settings, the node ID, the shared
170 secret and the TLS certificate.
171
172 =over 4
173
174 =item setnodeid <nodeid>
175
176 Set the node ID to the given string.
177
178 =item delnodeid
179
180 Removes the node ID again, which means it is inherited again from it's
181 parent profile, or stays unset.
182
183 =item gensecret
184
185 Generates a random shared secret and sets it. The shared secret is used to
186 authenticate nodes to each other when TLS is not required.
187
188 =item setsecret <secret>
189
190 Sets the shared secret tot he given string, which can be anything.
191
192 =item delsecret
193
194 Removes the shared secret again, which means it is inherited again from
195 it's parent profile, or stays unset.
196
197 =item gencert
198
199 Generates a self-signed certificate and key, and sets it. This works
200 similarly to a shared secret: when all nodes have it, TLS will be used to
201 authenticate and encrypt all traffic.
202
203 =item setcert <file>
204
205 Set a node certificate (and optionally any CA certificates) from the given
206 file. The file must contain the key, followed by the certificate, followed
207 by any CA certificates you want to trust, all in PEM format.
208
209 See L<AnyEvent::TLS> for some more details - this sets the C<cert> and
210 C<ca_cert> options.
211
212 =item delcert
213
214 Removes the certificate(s) again, which means it is inherited again from
215 it's parent profile, or stays unset.
216
217 =back
218
219 =head2 CONFIGURATION/SEEDS
220
221 To discover the network you have to specify some seed addresses, which are
222 basically C<host:port> pairs where you expect some long-running nodes. It
223 does no harm to have a node as its own seed (they will eventually be
224 ignored).
225
226 =over 4
227
228 =item setseeds <host:port>,...
229
230 Sets or replaces the list of seeds, which must be specified as a
231 comma-separated list of C<host:port> pairs. The C<host> can be a hostname,
232 an IP address, or C<*> to signify all local host addresses. If the
233 C<:port> is omitted, then the default port of C<4040> is assumed.
234
235 An empty list is allowed.
236
237 Example: use C<doomed> with default port as only seednode.
238
239 aemp setseeds doomed
240
241 =item delseeds
242
243 Removes the seed list again, which means it is inherited again from it's
244 parent profile, or stays unset.
245
246 =item addseed <host:port>
247
248 Adds a single seed address.
249
250 =item delseed <host:port>
251
252 Deletes the given seed address, if it exists.
253
254 =back
255
256 =head2 CONFIGURATION/BINDS
257
258 To be able to be reached from other nodes, a node must I<bind> itself
259 to some listening socket(s). The list of these can either bs specified
260 manually, or AnyEvent::MP can guess them. Nodes without any binds are
261 possible to some extent.
262
263 =over 4
264
265 =item setbinds <host:port>,...
266
267 Sets the list of bind addresses explicitly - see the F<aemp setseeds>
268 command for the exact syntax. In addition, a value of C<0> for the port
269 means to use a dynamically-assigned port.
270
271 Note that the C<*>, C<*:*> or C<*:port> values are very useful here.
272
273 Example: bind on the default port (4040) on all local interfaces.
274
275 aemp setbinds "*"
276
277 Example: bind on a random port on all local interfaces.
278
279 aemp setbinds "*:*"
280
281 Example: resolve "doomed.mydomain" and try to bind on port C<4040> of all
282 IP addressess returned.
283
284 aep setbinds doomed.mydomain
285
286 =item delbinds
287
288 Removes the bind list again, which means it is inherited again from it's
289 parent profile, or stays unset.
290
291 =item addbind <host:port>
292
293 Adds a single bind address.
294
295 =item delbind <host:port>
296
297 Deletes the given bind address, if it exists.
298
299 =back
300
301 =head2 CONFIGURATION/SERVICES
302
303 Services are modules (or functions) that are automatically loaded (or
304 executed) when a node starts. They are especially useful when used in
305 conjunction with F<aemp run>, to configure which services a node should
306 run.
307
308 =over 4
309
310 =item setservices <initfunc>...
311
312 Sets or replaces the list of services, which must be specified as a
313 comma-separated list.
314
315 Each entry in the list is interpreted as either a module name to
316 load (when it ends with C<::>) or a function to call (all other
317 cases). The algorithm to find the function is the same as used for C<<
318 L<AnyEvent::MP>::spawn >>.
319
320 =item delservices
321
322 Removes the service list again, which means it is inherited again from
323 it's parent profile, or stays unset.
324
325 =item addservice <initfunc>
326
327 Adds a single service.
328
329 =item delservice <initfunc>
330
331 Deletes the given service, if it exists.
332
333 =back
334
335 =head2 CONFIGURATION/PROFILE MANAGEMENT
336
337 All the above configuration functions by default affect the I<global
338 default configuration>, which is basically used to augment every profile
339 and node configuration.
340
341 =over 4
342
343 =item profile <name> ...
344
345 This subcommand makes the following subcommands act only on a specific
346 named profile, instead of on the global default. The profile is created if
347 necessary.
348
349 Example: create a C<server> profile, give it a random node name, some seed
350 nodes and bind it on an unspecified port on all local interfaces. You
351 should add some services then and run the node...
352
353 aemp profile server nodeid anon/ seeds doomed,10.0.0.2:5000 binds "*:*"
354
355 =item delprofile <name>
356
357 Deletes the profile of the given name.
358
359 =item setparent <name>
360
361 Sets the parent profile to use - values not specified in a profile will be
362 taken from the parent profile (even recursively, with the global default
363 config being the default parent). This is useful to configure profile
364 I<classes> and then to inherit from them for individual nodes.
365
366 Note that you can specify circular parent chains and even a parent for the
367 global configuration. Neither will do you any good, however.
368
369 Example: inherit all values not specified in the C<doomed> profile from
370 the C<server> profile.
371
372 aemp profile doomed setparent server
373
374 =item delparent
375
376 Removes the parent again from the profile, if any was set, so the profile
377 inherits directly from the global default config again.
378
379 =item showprofile <name>
380
381 Shows the values of the given profile, and only those, no inherited
382 values.
383
384 =item showconfig <name> <key value...>
385
386 Shows the I<effective> config, i.e. the values as used by a node started
387 with the given profile name. Any additional key-value pairs specified
388 augment the configuration, just as with C<configure>.
389
390 If all arguments are omitted, show the global default config.
391
392 =back
393
394 =cut
395
396 use common::sense;
397
398 # should come before anything else, so all modules
399 # will be loaded on each restart
400 BEGIN {
401 if ($ARGV[0] eq "run") {
402 shift;
403
404 # d'oh
405 require AnyEvent::Watchdog;
406 # now we can load extra modules
407
408 require AnyEvent::Watchdog::Util;
409 AnyEvent::Watchdog::Util::autorestart (1);
410 AnyEvent::Watchdog::Util::heartbeat (300);
411
412 require AnyEvent;
413 require AnyEvent::MP::Kernel;
414 AnyEvent::MP::Kernel::configure (@ARGV);
415
416 AnyEvent::detect () eq "AnyEvent::Impl::EV"
417 ? EV::loop ()
418 : AE::cv ()->recv;
419 }
420 }
421
422 use Carp ();
423
424 use JSON::XS;
425
426 use AnyEvent;
427 use AnyEvent::Util;
428
429 use AnyEvent::MP;
430 use AnyEvent::MP::Config;
431
432 sub my_run_cmd {
433 my ($cmd) = @_;
434
435 my $cv = &run_cmd;
436 my $status = $cv->recv;
437
438 $status
439 and die "@$cmd: command failed with exit status $status.";
440 }
441
442 sub gen_cert {
443 my_run_cmd [qw(openssl req
444 -new -nodes -x509 -days 3650
445 -newkey rsa:2048 -keyout /dev/fd/3
446 -batch -subj /CN=AnyEvent::MP
447 )],
448 "<", "/dev/null",
449 ">" , \my $cert,
450 "3>", \my $key,
451 "2>", "/dev/null";
452
453 "$cert$key"
454 }
455
456 sub init {
457 configure profile => "aemp", nodeid => "anon/";
458 }
459
460 our $cfg = AnyEvent::MP::Config::config;
461 our $profile = $cfg;
462
463 sub trace {
464 my ($node) = @_;
465 my $cv = AE::cv;
466 my %seen;
467
468 my $to = AE::timer 15, 0, sub { exit 1 };
469
470 init;
471
472 my $reply = port {
473 my ($node, @neigh) = @_;
474
475 @neigh = grep $_ ne $NODE, @neigh;
476
477 print +(join " ", $node, @neigh), "\n";
478
479 for (@neigh) {
480 unless ($seen{$_}++) {
481 $cv->begin;
482 snd $_, up_nodes => $SELF => $_;
483 }
484 }
485
486 $cv->end;
487 };
488
489 $cv->begin;
490 snd $reply, seed => $node;
491
492 $cv->recv;
493 }
494
495 sub docmd;
496
497 our %CMD = (
498 snd => sub {
499 my $port = shift @ARGV;
500 init;
501
502 snd $port, @ARGV; @ARGV = ();
503
504 my $cv = AE::cv;
505 my $to = AE::timer 5, 0, sub { exit 1 };
506 mon $port, $cv;
507 my $reply = port sub { &$cv };
508 snd node_of $port, snd => $reply, "message sent successfully";
509
510 print join " ", $cv->recv, "\n";
511 },
512
513 rpc => sub {
514 my $port = shift @ARGV;
515 init;
516
517 my $cv = AE::cv;
518 my $to = AE::timer 5, 0, sub { exit 1 };
519 snd $port, @ARGV, port { &$cv }; @ARGV = ();
520 mon $port, $cv;
521
522 print +(substr JSON::XS->new->encode ([$cv->recv]), 1, -1), "\n";
523 },
524
525 mon => sub {
526 my $port = shift @ARGV;
527 init;
528
529 mon $port, my $cv = AE::cv;
530 print join " ", $cv->recv, "\n";
531 },
532
533 eval => sub {
534 my $node = node_of shift @ARGV;
535 my $expr = join " ", @ARGV; @ARGV = ();
536 init;
537
538 my $cv = AE::cv;
539 my $to = AE::timer 5, 0, sub { exit 1 };
540 AnyEvent::MP::Kernel::eval_on $node, $expr, port { &$cv };
541 mon $node, $cv;
542
543 my ($err, @res) = $cv->recv;
544
545 die $err if length $err;
546
547 print +(substr JSON::XS->new->encode (\@res), 1, -1), "\n";
548 },
549
550 trace => sub {
551 @ARGV >= 1
552 or die "node id missing\n";
553
554 trace shift @ARGV;
555 },
556
557 setnodeid => sub {
558 @ARGV >= 1
559 or die "shared secret missing\n";
560
561 $profile->{nodeid} = shift @ARGV;
562 ++$cfg->{dirty};
563 },
564 delnodeid => sub {
565 delete $profile->{nodeid};
566 ++$cfg->{dirty};
567 },
568
569 setsecret => sub {
570 @ARGV >= 1
571 or die "shared secret missing\n";
572
573 $profile->{secret} = shift @ARGV;
574 ++$cfg->{dirty};
575 },
576 gensecret => sub {
577 $profile->{secret} = AnyEvent::MP::Kernel::alnumbits AnyEvent::MP::Kernel::nonce 64;
578 ++$cfg->{dirty};
579 },
580 delsecret => sub {
581 delete $profile->{secret};
582 ++$cfg->{dirty};
583 },
584
585 setcert => sub {
586 @ARGV >= 1
587 or die "key+certificate pem filename missing\n";
588
589 open my $fh, "<", $ARGV[0]
590 or die "$ARGV[0]: $!";
591
592 local $/;
593 $profile->{cert} = <$fh>;
594 ++$cfg->{dirty};
595 },
596 gencert => sub {
597 $profile->{cert} = gen_cert;
598 ++$cfg->{dirty};
599 },
600 delcert => sub {
601 delete $profile->{cert};
602 ++$cfg->{dirty};
603 },
604
605 setbinds => sub {
606 @ARGV >= 1
607 or die "bind addresses missing\n";
608 $profile->{binds} = [split /,/, shift @ARGV];
609 ++$cfg->{dirty};
610 },
611 delbinds => sub {
612 delete $profile->{binds};
613 ++$cfg->{dirty};
614 },
615 addbind => sub {
616 @ARGV >= 1
617 or die "bind address missing\n";
618 my $bind = shift @ARGV;
619
620 @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
621 push @{ $profile->{binds} }, $bind;
622 ++$cfg->{dirty};
623 },
624 delbind => sub {
625 @ARGV >= 1
626 or die "bind address missing\n";
627 my $bind = shift @ARGV;
628
629 @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
630 ++$cfg->{dirty};
631 },
632
633 setseeds => sub {
634 @ARGV >= 1
635 or die "seed addresses missing\n";
636 $profile->{seeds} = [split /,/, shift @ARGV];
637 ++$cfg->{dirty};
638 },
639 delseeds => sub {
640 delete $profile->{seeds};
641 ++$cfg->{dirty};
642 },
643 addseed => sub {
644 @ARGV >= 1
645 or die "seed address missing\n";
646 my $seed = shift @ARGV;
647
648 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
649 push @{ $profile->{seeds} }, $seed;
650 ++$cfg->{dirty};
651 },
652 delseed => sub {
653 @ARGV >= 1
654 or die "seed address missing\n";
655 my $seed = shift @ARGV;
656
657 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
658 ++$cfg->{dirty};
659 },
660
661 setservices => sub {
662 @ARGV >= 1
663 or die "service specifications missing\n";
664 $profile->{services} = [split /,/, shift @ARGV];
665 ++$cfg->{dirty};
666 },
667 delservices => sub {
668 delete $profile->{services};
669 ++$cfg->{dirty};
670 },
671 addservice => sub {
672 @ARGV >= 1
673 or die "service specification missing\n";
674 my $service = shift @ARGV;
675 push @{ $profile->{services} }, $service;
676 ++$cfg->{dirty};
677 },
678 delservice => sub {
679 @ARGV >= 1
680 or die "service specification missing\n";
681 my $service = shift @ARGV;
682 for (0 .. $#{ $profile->{services} }) {
683 next unless $profile->{services}[$_] eq $service;
684 splice @{ $profile->{services} }, $_, 1;
685 last;
686 }
687 ++$cfg->{dirty};
688 },
689
690 profile => sub {
691 @ARGV >= 1
692 or die "profile name is missing\n";
693 my $name = shift @ARGV;
694
695 $profile = $cfg->{profile}{$name} ||= {};
696 ++$cfg->{dirty};
697 },
698 delprofile => sub {
699 @ARGV >= 1
700 or die "profile name is missing\n";
701 my $name = shift @ARGV;
702
703 delete $cfg->{profile}{$name};
704 ++$cfg->{dirty};
705 },
706 setparent => sub {
707 @ARGV >= 1
708 or die "profile name is missing\n";
709
710 $profile->{parent} = shift @ARGV;
711 ++$cfg->{dirty};
712 },
713 delparent => sub {
714 delete $profile->{parent};
715 ++$cfg->{dirty};
716 },
717 showprofile => sub {
718 @ARGV >= 1
719 or die "profile name is missing\n";
720 my $name = shift @ARGV;
721
722 print JSON::XS->new->pretty->encode ($cfg->{profile}{$name} || {});
723 },
724 showconfig => sub {
725 my $name = @ARGV ? shift @ARGV : AnyEvent::MP::Kernel::_nodename;
726
727 my $profile = AnyEvent::MP::Config::find_profile $name, @ARGV;
728 @ARGV = ();
729
730 # make it look nicer:
731 delete $profile->{profile};
732 delete $profile->{parent};
733
734 print JSON::XS->new->pretty->encode ($profile);
735 },
736
737 # undocumented
738 _resolve => sub {
739 print +(join ",", (AnyEvent::MP::Kernel::_resolve shift @ARGV)->recv), "\n";
740 },
741 );
742
743 for (keys %CMD) {
744 $CMD{$1} = $CMD{$_} if /^set(.*)$/;
745 }
746
747 sub docmd {
748 my $cmd = shift @ARGV;
749
750 $CMD{$cmd}
751 or die "$cmd: no such aemp command (try perldoc aemp, or man aemp)";
752
753 $CMD{$cmd}();
754 }
755
756 @ARGV
757 or die "Usage: aemp subcommand ... (try perldoc aemp, or man aemp)\n";
758
759 docmd while @ARGV;
760
761