ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.32
Committed: Thu Sep 3 07:57:31 2009 UTC (14 years, 8 months ago) by elmex
Branch: MAIN
Changes since 1.31: +5 -5 lines
Log Message:
fixed some typos in documentation.

File Contents

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