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