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