ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.67
Committed: Sun Aug 28 15:38:42 2016 UTC (7 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-2_02, rel-2_01, rel-2_0, HEAD
Changes since 1.66: +2 -2 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 cal <port> <arg...> # send message, append reply
15 aemp eval <node> <expr...> # evaluate expression
16 aemp shell [<node>] # run an interactive shell
17 aemp trace <node> # trace the network topology
18
19 # run a node
20 aemp run configure_args... # run a node
21 aemp restart <node> # restart a node if running under watchdog
22
23 # node configuration: node ID
24 aemp setnodeid <nodeid> # configure the real node id
25 aemp delnodeid # reset node id to default (= inherit)
26
27 # node configuration: authentication
28 aemp gensecret # generate a random shared secret
29 aemp setsecret <secret> # set the shared secret
30 aemp delsecret # remove the secret (= inherit)
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 management
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 # node configuration: low-level protocol
62 aemp [set|del]secure <boolean>
63 aemp [set|del]monitor_timeout <seconds>
64 aemp [set|del]connect_interval <seconds>
65 aemp [set|del]framing_format [array]
66 aemp [set|del]auth_offer [array]
67 aemp [set|del]auth_accept [array]
68 aemp [set|del]autocork <boolean>
69 aemp [set|del]nodelay <boolean>
70
71 =head1 DESCRIPTION
72
73 With aemp you can configure various aspects of AnyEvent::MP and its
74 protocol, send various messages and even run a node.
75
76 The F<aemp> utility works like F<cvs>, F<svn> or other commands: the first
77 argument defines which operation (subcommand) is requested, after which
78 arguments for this operation are expected. When a subcommand does not eat
79 all remaining arguments, the remaining arguments will again be interpreted
80 as subcommand and so on.
81
82 This means you can chain multiple commands, which is handy for profile
83 configuration, e.g.:
84
85 aemp gensecret profile xyzzy binds 4040,4041 nodeid anon/
86
87 Please note that all C<setxxx> subcommands have an alias without the
88 C<set> prefix.
89
90 All configuration data is stored in a human-readable (JSON) config file
91 stored in F<~/.perl-anyevent-mp> (or F<%appdata%/perl-anyevent-mp> on
92 loser systems, or wherever C<$ENV{PERL_ANYEVENT_MP_RC}> points to). Feel
93 free to look at it or edit it, the format is relatively simple.
94
95 =head2 SPECIFYING ARGUMENTS
96
97 Arguments can be specified just as with any other shell command, with a
98 few special cases:
99
100 If the I<first> argument starts with a literal C<[>-character, then it is
101 interpreted as a UTF-8 encoded JSON text. The resulting array replaces all
102 arguments.
103
104 Otherwise, if I<any> argument starts with one of C<[>, C<{> or C<">, then
105 it is interpreted as UTF-8 encoded JSON text (or a single value in case of
106 C<">), and the resulting reference or scalar replaces the argument.
107
108 This allows you, for example, to specify binds in F<aemp run> (using POSIX
109 shell syntax):
110
111 aemp run binds '["*:4040"]'
112
113 =head2 RUNNING A NODE
114
115 This can be used to run a node - together with some services, this makes
116 it unnecessary to write any wrapper programs.
117
118 =over 4
119
120 =item run <configure_args>...
121
122 Runs a node by calling C<AnyEvent::MP::Kernel::configure> with the given
123 arguments. The node runs under L<AnyEvent::Watchdog>, can be restarted
124 (and autorestarted, see the L<AnyEvent::Watchdog> manual). A very common
125 invocation is to just specify a profile using the profile name
126
127 aemp run database-backend
128
129 ... but you can use most arguments that C<configure> understands:
130
131 aemp run nodeid mynode2 profile someprofile
132
133 Care has been taken to load (almost) no modules other than
134 L<AnyEvent::Watchdog> and the modules it loads, so everything (including
135 the L<AnyEvent::MP> modules themselves) will be freshly loaded on restart,
136 which makes upgrading everything except the perl binary easy.
137
138 =item restart <node>
139
140 Restarts the node using C<AnyEvent::Watchdog::Util::restart>. This works
141 for nodes started by C<aemp run>, but also for any other node that uses
142 L<AnyEvent::Watchdog>.
143
144 =back
145
146 =head2 PROTOCOL COMMANDS
147
148 These commands actually communicate with other nodes. They all use a node
149 profile name of C<aemp> (specifying a default node ID of C<anon/> and a
150 binds list containing C<*:*> only).
151
152 They all use a timeout of five seconds, after which they give up.
153
154 =over 4
155
156 =item snd <port> <arguments...>
157
158 Simply send a message to the given port - where you get the port ID from
159 is your problem.
160
161 Exits after ensuring that the message has been delivered to its node.
162
163 Most useful to take advantage of some undocumented functionality inside
164 nodes, such as node ports being able to call any method:
165
166 aemp snd doomed AnyEvent::Watchdog::restart 1
167
168 =item cal <port> <arg...>
169
170 Like F<aemp cal>: appends a local reply port to the message and waits for
171 a message to it.
172
173 Any return values will be JSON-encoded and printed separated by commas
174 (kind of like a JSON array without []-brackets).
175
176 Example: ask the (undocumented) time service of a node for its current
177 time.
178
179 aemp cal mynode time
180
181 =item mon <port>
182
183 Monitors the port and exits when it's monitorign callback is called. Most
184 useful to monitor node ports.
185
186 Example: monitor some node.
187
188 aemp mon doomed
189
190 =item eval <node> <expr...>
191
192 Joins all remaining arguments into a string and evaluates it on the given
193 node. Return values are handled as with F<aemp cal>.
194
195 Example: find the unix process ID of the node called posicks.
196
197 aemp eval posicks '$$'
198
199 =item trace <node>
200
201 Asks the given node for all currently connected nodes, then asks those
202 nodes for the same, thus tracing all node connections.
203
204 =back
205
206 =head2 CONFIGURATION/NODE ID/SECRET/CERTIFICATE
207
208 These commands deal with rather basic settings, the node ID, the shared
209 secret and the TLS certificate.
210
211 All C<setxxx> commands have an alias without the C<set> prefix (for
212 exmaple, C<setnodeid> can be shortened to C<nodeid>), unless that alias is
213 already used for somethign else (for exmaple, C<eval> is not an alias for
214 C<seteval>).
215
216 =over 4
217
218 =item setnodeid <nodeid>
219
220 Set the node ID to the given string. If it ends with a slash (C</>), then
221 a random string is appended to make it unique.
222
223 If no nodeid is specified in any profile, then the profile name, plus
224 appended slash, is used.
225
226 =item delnodeid
227
228 Removes the node ID again, which means it is inherited again from it's
229 parent profile, or stays unset.
230
231 =item gensecret
232
233 Generates a random shared secret (currently 1071 bits) and sets it. The
234 shared secret is used to authenticate nodes to each other when TLS is not
235 required.
236
237 =item setsecret <secret>
238
239 Sets the shared secret to the given string, which can be anything.
240
241 =item delsecret
242
243 Removes the shared secret again, which means it is inherited again from
244 it's parent profile, or stays unset.
245
246 =item gencert
247
248 Generates a self-signed certificate and key, and sets it. This works
249 similarly to a shared secret: when all nodes have it, TLS will be used to
250 authenticate and encrypt all traffic.
251
252 =item setcert <file>
253
254 Set a node certificate (and optionally any CA certificates) from the given
255 file. The file must contain the key, followed by the certificate, followed
256 by any CA certificates you want to trust, all in PEM format.
257
258 See L<AnyEvent::TLS> for some more details - this sets the C<cert> and
259 C<ca_cert> options.
260
261 =item delcert
262
263 Removes the certificate(s) again, which means it is inherited again from
264 it's parent profile, or stays unset.
265
266 =back
267
268 =head2 CONFIGURATION/SEEDS
269
270 To discover the network you have to specify some seed addresses, which are
271 basically C<host:port> pairs where you expect some long-running nodes. It
272 does no harm to have a node as its own seed (they will eventually be
273 ignored).
274
275 =over 4
276
277 =item setseeds <host:port>,...
278
279 Sets or replaces the list of seeds, which must be specified as a
280 comma-separated list of C<host:port> pairs. The C<host> can be a hostname,
281 an IP address, or C<*> to signify all local host addresses (which makes
282 little sense for seeds, outside some examples, but a lot of sense for
283 binds).
284
285 An empty list is allowed.
286
287 Example: use C<doomed> with default port as only seednode.
288
289 aemp setseeds doomed
290
291 =item delseeds
292
293 Removes the seed list again, which means it is inherited again from it's
294 parent profile, or stays unset.
295
296 =item addseed <host:port>
297
298 Adds a single seed address.
299
300 =item delseed <host:port>
301
302 Deletes the given seed address, if it exists.
303
304 =back
305
306 =head2 CONFIGURATION/BINDS
307
308 To be able to be reached from other nodes, a node must I<bind> itself
309 to some listening socket(s). The list of these can either bs specified
310 manually, or AnyEvent::MP can guess them. Nodes without any binds are
311 possible to some extent.
312
313 =over 4
314
315 =item setbinds <host:port>,...
316
317 Sets the list of bind addresses explicitly - see the F<aemp setseeds>
318 command for the exact syntax. In addition, a value of C<*> for the port,
319 or not specifying a port, means to use a dynamically-assigned port.
320
321 Note that the C<*>, C<*:*> or C<*:port> patterns are very useful here.
322
323 Example: bind on a ephemeral port on all local interfaces.
324
325 aemp setbinds "*"
326
327 Example: bind on a random port on all local interfaces.
328
329 aemp setbinds "*:*"
330
331 Example: resolve "doomed.mydomain" and try to bind on port C<4040> of all
332 IP addressess returned.
333
334 aep setbinds doomed.mydomain:4040
335
336 =item delbinds
337
338 Removes the bind list again, which means it is inherited again from it's
339 parent profile, or stays unset.
340
341 =item addbind <host:port>
342
343 Adds a single bind address.
344
345 =item delbind <host:port>
346
347 Deletes the given bind address, if it exists.
348
349 =back
350
351 =head2 CONFIGURATION/SERVICES
352
353 Services are modules (or functions) that are automatically loaded (or
354 executed) when a node starts. They are especially useful when used in
355 conjunction with F<aemp run>, to configure which services a node should
356 run.
357
358 Despite the daunting name, services really I<are> nothing more than a
359 module name or a function name with arguments. The "service" aspect comes
360 only from the behaviour of the module or function, which is supposed to
361 implement, well, some kind of service for the node, network etc.
362
363 Instead of writing a standalone program for each different node type in
364 your network, you can simply put your code into a module, and then let the
365 configuration decide which node runs which "services". This also makes it
366 easy to combine multiple different services within the same node.
367
368 =over 4
369
370 =item setservices <initfunc>...
371
372 Sets or replaces the list of services, which must be specified as a
373 comma-separated list or a JSON array.
374
375 Each string entry in the list is interpreted as either a module name to
376 load (when it ends with C<::>) or a function to call (all other cases).
377
378 Each entry which is an array itself (you need to use JSON format to
379 specify those) is interpreted as a function name and the arguments to
380 pass.
381
382 The algorithm to find the function is the same as used for C<<
383 L<AnyEvent::MP>::spawn >>.
384
385 Example: run the global service.
386
387 aemp setservices AnyEvent::MP::Global::
388
389 Example: call the mymod::myfun function with arguments 1, 2 and 3.
390
391 aemp setservices '[["mymod::myfun", 1,2,3]]'
392
393 =item delservices
394
395 Removes the service list again, which means it is inherited again from
396 it's parent profile, or stays unset.
397
398 =item addservice <initfunc>
399
400 Adds a single service.
401
402 =item delservice <initfunc>
403
404 Deletes the given service, if it exists.
405
406 =item seteval <string>
407
408 Sometimes, all you need is to evaluate a small perl snippet to bring a
409 node up. This sets a perl string that is eval'ed after the node has been
410 configured.
411
412 =item deleval
413
414 Delete any eval string set with seteval.
415
416 =back
417
418 =head2 CONFIGURATION/PROFILE MANAGEMENT
419
420 All the above configuration functions by default affect the I<global
421 default configuration>, which is basically used to augment every profile
422 and node configuration.
423
424 =over 4
425
426 =item profile <name> ...
427
428 This subcommand makes the following subcommands act only on a specific
429 named profile, instead of on the global default. The profile is created if
430 necessary.
431
432 Example: create a C<server> profile, give it a random node name, some seed
433 nodes and bind it on an unspecified port on all local interfaces. You
434 should add some services then and run the node...
435
436 aemp profile server nodeid anon/ seeds doomed,10.0.0.2:5000 binds "*:*"
437
438 =item delprofile <name>
439
440 Deletes the profile of the given name.
441
442 =item setparent <name>
443
444 Sets the parent profile to use - values not specified in a profile will be
445 taken from the parent profile (even recursively, with the global default
446 config being the default parent). This is useful to configure profile
447 I<classes> and then to inherit from them for individual nodes.
448
449 Note that you can specify circular parent chains and even a parent for the
450 global configuration. Neither will do you any good, however.
451
452 Example: inherit all values not specified in the C<doomed> profile from
453 the C<server> profile.
454
455 aemp profile doomed setparent server
456
457 =item delparent
458
459 Removes the parent again from the profile, if any was set, so the profile
460 inherits directly from the global default config again.
461
462 =item showprofile <name>
463
464 Shows the values of the given profile, and only those, no inherited
465 values.
466
467 =item showconfig <name> <key value...>
468
469 Shows the I<effective> config, i.e. the values as used by a node started
470 with the given profile name. Any additional key-value pairs specified
471 augment the configuration, just as with C<configure>.
472
473 If all arguments are omitted, show the global default config.
474
475 =back
476
477 =head2 LOW-LEVEL TRANSPORT PROTOCOL
478
479 The low-level transport protocol betwene two nodes also has a number of
480 configurable options, most of which should not be touched unless you know
481 what you are doing.
482
483 =over 4
484
485 =item [set|del]secure <boolean>
486
487 Normally, nodes allow anything to be done to them by remote nodes,
488 including remotely-triggered execution of code.
489
490 Sometimes a more secure mode is desired - this can be achieved by setting
491 the secure option to a true value.
492
493 When secure mode is enabled, then the node will not execute code locally,
494 at least not via the normal node protocol. All other messages are still
495 allowed. This means remote nodes can monitor, kill or snd to local ports
496 (port names can also be easily guessed).
497
498 At the moment, this setting ignores C<snd_on>, C<eval_on> and C<spawn>
499 requests.
500
501 =item [set|del]monitor_timeout <seconds>
502
503 Sets the default monitor timeout, that is, when a connection to a node
504 cannot be established within this many seconds, the node is declared
505 unreachable and all monitors will fire.
506
507 C<30> seconds are usually a good time span for this.
508
509 =item [set|del]connect_interval <seconds>
510
511 When a connection cannot be established successfully within this many
512 seconds, try the next transport address (e.g. the next IP address). If
513 your nodes have a lot of transports, you might have to set this to a low
514 value so that they will actually all be tried within the monitor timeout
515 interval.
516
517 C<2> is usually a good value, unless you live in new zealand.
518
519 =item [set|del]framing_format [array]
520
521 Configures the list of framing formats offered to the other side. This is
522 simply a list of formatted read/write types used with L<AnyEvent::Handle>,
523 in order of decreasing preference.
524
525 Nodes support C<cbor>, C<json> and C<storable> framing formats for data
526 packets out of the box, and usually choose C<cbor> because it is first in
527 the list.
528
529 Example: prefer the C<My::Personal::Format> framing format over JSON over
530 Storable.
531
532 aemp setframing_format '["My::Personal::Format", "json", "storable"]'
533
534 =item [set|del]auth_offer [array]
535
536 Configures the list of authentication types that the node offers to the
537 other side as acceptable, in order of decreasing preference. Only auth
538 methods that the node can actually support will be offered.
539
540 The default is '["tls_sha3_512", "hmac_sha3_512"]' and is usually good
541 enough.
542
543 =item [set|del]auth_accept [array]
544
545 Configures the list of authentication types that remote nodes can use to
546 authenticate, in order of decreasing preference.
547
548 The default is '["tls_sha3_512", "hmac_sha3_512", "tls_anon",
549 "cleartext"]' and is usually good enough.
550
551 =item [set|del]autocork <boolean>
552
553 Sets the default C<autocork> option value for the L<AnyEvent::Handle>
554 object used by transports. By default, autocorking is off, potentially
555 reducing latency.
556
557 =item [set|del]nodelay <boolean>
558
559 Sets the default C<nodelay> option value for the L<AnyEvent::Handle>
560 object used by transports. By default, nodelay is on, potentially reducing
561 latency.
562
563 =back
564
565 =cut
566
567 use common::sense;
568
569 # should come before anything else, so all modules
570 # will be loaded on each restart
571 BEGIN {
572 if (@ARGV == 1 && $ARGV[0] =~ /^\[/) {
573 require JSON::XS;
574 @ARGV = @{ JSON::XS->new->utf8->decode (shift) };
575 } else {
576 for (@ARGV) {
577 if (/^[\[\{\"]/) {
578 require JSON::XS;
579 $_ = JSON::XS->new->utf8->allow_nonref->decode ($_);
580 }
581 }
582 }
583
584 if ($ARGV[0] eq "run") {
585 shift;
586
587 # d'oh
588 require AnyEvent::Watchdog;
589 # only now can we load additional modules
590
591 require AnyEvent;
592
593 require AnyEvent::Watchdog::Util;
594 AnyEvent::Watchdog::Util::autorestart (1);
595 AnyEvent::Watchdog::Util::heartbeat (300);
596
597 require AnyEvent::MP::Kernel;
598 AnyEvent::MP::Kernel::configure (@ARGV);
599
600 AnyEvent::detect () eq "AnyEvent::Impl::EV"
601 ? EV::loop ()
602 : AE::cv ()->recv;
603 }
604 }
605
606 use Carp ();
607
608 use JSON::XS;
609
610 use AnyEvent;
611 use AnyEvent::Util;
612
613 use AnyEvent::MP;
614 use AnyEvent::MP::Config;
615
616 sub my_run_cmd {
617 my ($cmd) = @_;
618
619 my $cv = &run_cmd;
620 my $status = $cv->recv;
621
622 $status
623 and die "@$cmd: command failed with exit status $status.";
624 }
625
626 sub gen_cert {
627 my_run_cmd [qw(openssl req
628 -new -nodes -x509 -days 3650
629 -newkey rsa:2048 -keyout /dev/fd/3
630 -batch -subj /CN=AnyEvent::MP
631 )],
632 "<", "/dev/null",
633 ">" , \my $cert,
634 "3>", \my $key,
635 "2>", "/dev/null";
636
637 "$cert$key"
638 }
639
640 sub init {
641 configure profile => "aemp", nodeid => "aemp/%n/%u";
642 }
643
644 our $cfg = AnyEvent::MP::Config::config;
645 our $profile = $cfg;
646
647 sub trace {
648 my ($seed) = @_;
649 my $cv = AE::cv;
650 my %seen;
651 my $exit;
652
653 my %to;
654
655 init;
656
657 my $reply = port {
658 my ($node, undef, @neigh) = @_;
659
660 delete $to{$node};
661
662 @neigh = grep $_ ne $NODE, @neigh;
663
664 print $node, " -> ", (join " ", @neigh), "\n";
665
666 for my $neigh (@neigh) {
667 unless ($seen{$neigh}++) {
668 $cv->begin;
669 $to{$neigh} = AE::timer 15, 0, sub {
670 print "$neigh (timeout)\n";
671 $exit = 1;
672 $cv->end;
673 };
674 AnyEvent::MP::Kernel::eval_on $neigh, "AnyEvent::MP::Kernel::up_nodes" => $SELF => $neigh;
675 }
676 }
677
678 $cv->end;
679 };
680
681 $cv->begin;
682 snd $reply, seed => undef, $seed;
683
684 $cv->recv;
685
686 exit $exit;
687 }
688
689 sub shell {
690 init;
691
692 my $node = shift @ARGV || $NODE;
693 $| = 1;
694
695 print <<EOF;
696 Entering interactive shell - no commandline editing of course (use rlfe etc.).
697
698 \= display a list of nodes
699 \=name switch to another node
700 package P switch to package P when evaluating
701 \$ECHO contains the name of a port that echos everything sent to it
702
703 EOF
704 my $json = JSON::XS->new->pretty->ascii;
705 my $pkg = "AnyEvent::MP::Kernel";
706 my $cv = AE::cv;
707 my $echo = port {
708 print "\nECHO<$AnyEvent::MP::Kernel::SRCNODE> ", $json->encode (\@_), "\n$node $pkg> ";
709 };
710 print "$node $pkg> ";
711 my $t = AE::io *STDIN, 0, sub {
712 chomp (my $line = <STDIN>);
713
714 if ($line =~ s/^=//) {
715 if (length $line) {
716 $node = $line;
717 } else {
718 db_keys "'l" => sub {
719 print "\nnodes: ", (join " ", sort @{ $_[0] }), "\n$node $pkg> ";
720 };
721 }
722 } elsif ($line =~ /^\s*package\s+(\S+)\s*;?\s*$/) {
723 $pkg = $1;
724 } elsif ($line =~ /\S/) {
725 my $time = AE::time;
726 AnyEvent::MP::Kernel::eval_on
727 $node,
728 "package $pkg; my \$ECHO = '$echo'; $line",
729 port {
730 kil $SELF;
731
732 my ($err, @res) = @_;
733
734 $time = AE::time - $time;
735
736 print "\n$node: $line\n";
737 printf "%0.3fs\n", $time;
738 if (length $err) {
739 print "$err @res";
740 } else {
741 print $json->encode(\@res);
742 }
743 print "\n$node $pkg> ";
744 }
745 ;
746 }
747
748 print "$node $pkg> ";
749 };
750 $cv->recv;
751 }
752
753 sub node_eval {
754 my ($node, $expr) = @_;
755
756 init;
757
758 my $cv = AE::cv;
759 my $to = AE::timer 5, 0, sub { exit 1 };
760 AnyEvent::MP::Kernel::eval_on $node, $expr, port { &$cv };
761 mon $node, $cv;
762
763 my ($err, @res) = $cv->recv;
764
765 die "$err @res" if length $err;
766
767 print +(substr JSON::XS->new->encode (\@res), 1, -1), "\n";
768 }
769
770 sub docmd;
771
772 our %CMD = (
773 snd => sub {
774 my $port = shift @ARGV;
775 init;
776
777 snd $port, @ARGV; @ARGV = ();
778
779 my $cv = AE::cv;
780 my $to = AE::timer 5, 0, sub { exit 1 };
781 mon $port, $cv;
782 my $reply = port sub { &$cv };
783 snd node_of $port, snd => $reply, "message sent successfully";
784
785 print join " ", $cv->recv, "\n";
786 },
787
788 cal => sub {
789 my $port = shift @ARGV;
790 init;
791
792 my $cv = AE::cv;
793 cal $port, @ARGV, sub { &$cv }; @ARGV = ();
794
795 print +(substr JSON::XS->new->encode ([$cv->recv]), 1, -1), "\n";
796 },
797
798 mon => sub {
799 my $port = shift @ARGV;
800 init;
801
802 mon $port, my $cv = AE::cv;
803 print join " ", $cv->recv, "\n";
804 },
805
806 eval => sub {
807 my $node = node_of shift @ARGV;
808 my $expr = join " ", @ARGV; @ARGV = ();
809 node_eval $node, $expr;
810 },
811
812 shell => \&shell,
813
814 trace => sub {
815 @ARGV >= 1
816 or die "node id missing\n";
817
818 trace shift @ARGV;
819 },
820 restart => sub {
821 my $node = node_of shift @ARGV;
822 node_eval $node, 'my $w; $w = AE::idle sub { '
823 . 'undef $w; '
824 . 'use AnyEvent::Watchdog::Util ();'
825 . 'AnyEvent::Watchdog::Util::restart'
826 . '}; ()';
827 },
828
829 setnodeid => sub {
830 @ARGV >= 1
831 or die "shared secret missing\n";
832
833 $profile->{nodeid} = shift @ARGV;
834 ++$cfg->{dirty};
835 },
836 delnodeid => sub {
837 delete $profile->{nodeid};
838 ++$cfg->{dirty};
839 },
840
841 setsecret => sub {
842 @ARGV >= 1
843 or die "shared secret missing\n";
844
845 $profile->{secret} = shift @ARGV;
846 ++$cfg->{dirty};
847 },
848 gensecret => sub {
849 $profile->{secret} = AnyEvent::MP::Kernel::nonce62 180; # ~1071 bits
850 ++$cfg->{dirty};
851 },
852 delsecret => sub {
853 delete $profile->{secret};
854 ++$cfg->{dirty};
855 },
856
857 setcert => sub {
858 @ARGV >= 1
859 or die "key+certificate pem filename missing\n";
860
861 my $certfile = shift @ARGV;
862
863 open my $fh, "<", $certfile
864 or die "$certfile: $!";
865
866 local $/;
867 $profile->{cert} = <$fh>;
868 ++$cfg->{dirty};
869 },
870 gencert => sub {
871 $profile->{cert} = gen_cert;
872 ++$cfg->{dirty};
873 },
874 delcert => sub {
875 delete $profile->{cert};
876 ++$cfg->{dirty};
877 },
878
879 setbinds => sub {
880 @ARGV >= 1
881 or die "bind addresses missing\n";
882 my $list = shift @ARGV;
883 $profile->{binds} = ref $list ? $list : [split /,/, $list];
884 ++$cfg->{dirty};
885 },
886 delbinds => sub {
887 delete $profile->{binds};
888 ++$cfg->{dirty};
889 },
890 addbind => sub {
891 @ARGV >= 1
892 or die "bind address missing\n";
893 my $bind = shift @ARGV;
894
895 @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
896 push @{ $profile->{binds} }, $bind;
897 ++$cfg->{dirty};
898 },
899 delbind => sub {
900 @ARGV >= 1
901 or die "bind address missing\n";
902 my $bind = shift @ARGV;
903
904 @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
905 ++$cfg->{dirty};
906 },
907
908 setseeds => sub {
909 @ARGV >= 1
910 or die "seed addresses missing\n";
911 my $list = shift @ARGV;
912 $profile->{seeds} = ref $list ? $list : [split /,/, $list];
913 ++$cfg->{dirty};
914 },
915 delseeds => sub {
916 delete $profile->{seeds};
917 ++$cfg->{dirty};
918 },
919 addseed => sub {
920 @ARGV >= 1
921 or die "seed address missing\n";
922 my $seed = shift @ARGV;
923
924 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
925 push @{ $profile->{seeds} }, $seed;
926 ++$cfg->{dirty};
927 },
928 delseed => sub {
929 @ARGV >= 1
930 or die "seed address missing\n";
931 my $seed = shift @ARGV;
932
933 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
934 ++$cfg->{dirty};
935 },
936
937 setservices => sub {
938 @ARGV >= 1
939 or die "service specifications missing\n";
940 my $list = shift @ARGV;
941 $profile->{services} = ref $list ? $list : [split /,/, $list];
942 ++$cfg->{dirty};
943 },
944 delservices => sub {
945 delete $profile->{services};
946 ++$cfg->{dirty};
947 },
948 addservice => sub {
949 @ARGV >= 1
950 or die "service specification missing\n";
951 my $service = shift @ARGV;
952 push @{ $profile->{services} }, $service;
953 ++$cfg->{dirty};
954 },
955 delservice => sub {
956 @ARGV >= 1
957 or die "service specification missing\n";
958 my $service = shift @ARGV;
959 for (0 .. $#{ $profile->{services} }) {
960 next unless $profile->{services}[$_] eq $service;
961 splice @{ $profile->{services} }, $_, 1;
962 last;
963 }
964 ++$cfg->{dirty};
965 },
966 seteval => sub {
967 @ARGV >= 1
968 or die "eval string missing\n";
969
970 $profile->{eval} = shift @ARGV;
971 ++$cfg->{dirty};
972 },
973 deleval => sub {
974 delete $profile->{eval};
975 ++$cfg->{dirty};
976 },
977
978 profile => sub {
979 @ARGV >= 1
980 or die "profile name is missing\n";
981 my $name = shift @ARGV;
982
983 $profile = $cfg->{profile}{$name} ||= {};
984 ++$cfg->{dirty};
985 },
986 delprofile => sub {
987 @ARGV >= 1
988 or die "profile name is missing\n";
989 my $name = shift @ARGV;
990
991 delete $cfg->{profile}{$name};
992 ++$cfg->{dirty};
993 },
994 setparent => sub {
995 @ARGV >= 1
996 or die "profile name is missing\n";
997
998 $profile->{parent} = shift @ARGV;
999 ++$cfg->{dirty};
1000 },
1001 delparent => sub {
1002 delete $profile->{parent};
1003 ++$cfg->{dirty};
1004 },
1005 showprofile => sub {
1006 @ARGV >= 1
1007 or die "profile name is missing\n";
1008 my $name = shift @ARGV;
1009
1010 print JSON::XS->new->pretty->encode ($cfg->{profile}{$name} || {});
1011 },
1012 showconfig => sub {
1013 my $name = @ARGV ? shift @ARGV : AnyEvent::MP::Kernel::nodename;
1014
1015 my $profile = AnyEvent::MP::Config::find_profile $name, @ARGV;
1016 @ARGV = ();
1017
1018 # make it look nicer:
1019 delete $profile->{profile};
1020 delete $profile->{parent};
1021
1022 print JSON::XS->new->pretty->encode ($profile);
1023 },
1024 );
1025
1026 for my $attr (qw(
1027 monitor_timeout connect_interval framing_format auth_offer
1028 auth_accept autocork nodelay secure
1029 )) {
1030 $CMD{"set$attr"} = sub {
1031 @ARGV >= 1
1032 or die "$attr value is missing\n";
1033
1034 $profile->{$attr} = shift @ARGV;
1035 ++$cfg->{dirty};
1036 };
1037 $CMD{"del$attr"} = sub {
1038 delete $profile->{$attr};
1039 ++$cfg->{dirty};
1040 };
1041 }
1042
1043 for (keys %CMD) {
1044 $CMD{$1} ||= $CMD{$_} if /^set(.*)$/;
1045 }
1046
1047 sub docmd {
1048 my $cmd = shift @ARGV;
1049
1050 $CMD{$cmd}
1051 or die "$cmd: no such aemp command (try perldoc aemp, or man aemp)";
1052
1053 $CMD{$cmd}();
1054 }
1055
1056 @ARGV
1057 or die "Usage: aemp subcommand ... (try perldoc aemp, or man aemp)\n";
1058
1059 docmd while @ARGV;
1060
1061