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