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