ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.36
Committed: Fri Sep 4 21:46:10 2009 UTC (14 years, 9 months ago) by root
Branch: MAIN
Changes since 1.35: +8 -7 lines
Log Message:
*** empty log message ***

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