ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.30
Committed: Wed Sep 2 16:28:42 2009 UTC (14 years, 9 months ago) by root
Branch: MAIN
Changes since 1.29: +10 -3 lines
Log Message:
*** empty log message ***

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     it unnecesary to write any wrapper programs.
84    
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 root 1.25 profile name of C<aemp> (specifying a dfeault node ID of C<anon/> and a
111     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     Most useful to take avdantage of some undocumented functionality inside
125     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     Example: ask the (undocumented) time service of a node for it'S current
138     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     =cut
166    
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     Generates a self-signed certficate and key, and sets it. This works
200     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     =item setseeds <host:port>,...
227    
228     Sets or replaces the list of seeds, which must be specified as a
229     comma-separated list of C<host:port> pairs. The C<host> can be a hostname,
230     an IP address, or C<*> to signify all local host addresses. If the
231     C<:port> is omitted, then the default port of C<4040> is assumed.
232    
233     An empty list is allowed.
234    
235     Example: use C<doomed> with default port as only seednode.
236    
237     aemp setseeds doomed
238    
239     =item delseeds
240    
241     Removes the seed list again, which means it is inherited again from it's
242     parent profile, or stays unset.
243    
244     =item addseed <host:port>
245    
246     Adds a single seed address.
247    
248     =item delseed <host:port>
249    
250     Deletes the given seed address, if it exists.
251    
252     =back
253    
254     =head2 CONFIGURATION/BINDS
255    
256     To be able to be reached from other nodes, a node must I<bind> itself
257     to some listening socket(s). The list of these can either bs specified
258     manually, or AnyEvent::MP can guess them. Nodes without any binds are
259     possible to some extent.
260    
261     =over 4
262    
263     =item setbinds <host:port>,...
264    
265     Sets the list of bind addresses explicitly - see the F<aemp setseeds>
266     command for the exact syntax. In addition, a value of C<0> for the port
267     means to use a dynamically-assigned port.
268    
269 root 1.25 Note that the C<*>, C<*:*> or C<*:port> values are very useful here.
270 root 1.23
271     Example: bind on the default port (4040) on all local interfaces.
272    
273     aemp setbinds "*"
274    
275     Example: bind on a random port on all local interfaces.
276    
277 root 1.25 aemp setbinds "*:*"
278 root 1.23
279     Example: resolve "doomed.mydomain" and try to bind on port C<4040> of all
280     IP addressess returned.
281    
282     aep setbinds doomed.mydomain
283    
284     =item delbinds
285    
286     Removes the bind list again, which means it is inherited again from it's
287     parent profile, or stays unset.
288    
289     =item addbind <host:port>
290    
291     Adds a single bind address.
292    
293     =item delbind <host:port>
294    
295     Deletes the given bind address, if it exists.
296    
297     =back
298    
299     =head2 CONFIGURATION/SERVICES
300    
301     Services are modules (or functions) that are automatically loaded (or
302     executed) when a node starts. They are especially useful when used in
303     conjunction with F<aemp run>, to configure which services a node should
304     run.
305    
306     =over 4
307    
308     =item setservices <initfunc>...
309    
310     Sets or replaces the list of services, which must be specified as a
311     comma-separated list.
312    
313     Each entry in the list is interpreted as either a module name to
314     load (when it ends with C<::>) or a function to call (all other
315     cases). The algorithm to find the function is the same as used for C<<
316     L<AnyEvent::MP>::spawn >>.
317    
318     =item delservices
319    
320     Removes the service list again, which means it is inherited again from
321     it's parent profile, or stays unset.
322    
323     =item addservice <initfunc>
324    
325     Adds a single service.
326    
327     =item delservice <initfunc>
328    
329     Deletes the given service, if it exists.
330    
331     =back
332    
333     =head2 CONFIGURATION/PROFILE MANAGEMENT
334    
335     All the above configuration functions by default affect the I<global
336     default configuration>, which is basically used to augment every profile
337     and node configuration.
338    
339     =over 4
340    
341     =item profile <name> ...
342    
343     This subcommand makes the following subcommands act only on a specific
344     named profile, instead of on the global default. The profile is created if
345     necessary.
346    
347     Example: create a C<server> profile, give it a random node name, some seed
348     nodes and bind it on an unspecified port on all local interfaces. You
349     should add some services then and run the node...
350    
351 root 1.30 aemp profile server nodeid anon/ seeds doomed,10.0.0.2:5000 binds "*:*"
352 root 1.23
353     =item delprofile <name>
354    
355     Deletes the profile of the given name.
356    
357 root 1.26 =item setparent <name>
358 root 1.24
359     Sets the parent profile to use - values not specified in a profile will be
360     taken from the parent profile (even recursively, with the global default
361     config being the default parent). This is useful to configure profile
362     I<classes> and then to inherit from them for individual nodes.
363    
364     Note that you can specify circular parent chains and even a parent for the
365     global configuration. Neither will do you any good, however.
366    
367     Example: inherit all values not specified in the C<doomed> profile from
368     the C<server> profile.
369    
370 root 1.26 aemp profile doomed setparent server
371 root 1.24
372     =item delparent
373    
374     Removes the parent again from the profile, if any was set, so the profile
375     inherits directly from the global default config again.
376    
377 root 1.23 =item showprofile <name>
378    
379     Shows the values of the given profile, and only those, no inherited
380     values.
381    
382 root 1.25 =item showconfig <name> <key value...>
383 root 1.23
384 root 1.24 Shows the I<effective> config, i.e. the values as used by a node started
385 root 1.25 with the given profile name. Any additional key-value pairs specified
386 root 1.26 augment the configuration, just as with C<configure>.
387 root 1.24
388 root 1.25 If all arguments are omitted, show the global default config.
389 root 1.23
390     =back
391    
392 root 1.1 =cut
393    
394     use common::sense;
395    
396 root 1.19 # should come before anything else, so all modules
397     # will be loaded on each restart
398 root 1.22 BEGIN {
399 root 1.8 if ($ARGV[0] eq "run") {
400     shift;
401    
402 root 1.13 # d'oh
403 root 1.22 require AnyEvent::Watchdog;
404 root 1.19 # now we can load extra modules
405    
406 root 1.29 require AnyEvent::Watchdog::Util;
407     AnyEvent::Watchdog::Util::autorestart (1);
408     AnyEvent::Watchdog::Util::heartbeat (300);
409 root 1.13
410 root 1.8 require AnyEvent;
411 root 1.25 require AnyEvent::MP::Kernel;
412 root 1.26 AnyEvent::MP::Kernel::configure (@ARGV);
413 root 1.8
414 root 1.16 AnyEvent::detect () eq "AnyEvent::Impl::EV"
415 root 1.8 ? EV::loop ()
416     : AE::cv ()->recv;
417     }
418 root 1.22 }
419 root 1.8
420 root 1.1 use Carp ();
421    
422 root 1.17 use JSON::XS;
423    
424 root 1.1 use AnyEvent;
425 root 1.4 use AnyEvent::Util;
426    
427 root 1.8 use AnyEvent::MP;
428 root 1.1 use AnyEvent::MP::Config;
429    
430     sub my_run_cmd {
431     my ($cmd) = @_;
432    
433     my $cv = &run_cmd;
434     my $status = $cv->recv;
435    
436     $status
437     and die "@$cmd: command failed with exit status $status.";
438     }
439    
440     sub gen_cert {
441 root 1.2 my_run_cmd [qw(openssl req
442     -new -nodes -x509 -days 3650
443     -newkey rsa:2048 -keyout /dev/fd/3
444     -batch -subj /CN=AnyEvent::MP
445     )],
446 root 1.5 "<", "/dev/null",
447 root 1.1 ">" , \my $cert,
448     "3>", \my $key,
449 root 1.4 "2>", "/dev/null";
450 root 1.1
451     "$cert$key"
452     }
453    
454 root 1.25 sub init {
455 root 1.26 configure profile => "aemp", nodeid => "anon/";
456 root 1.25 }
457    
458 root 1.10 our $cfg = AnyEvent::MP::Config::config;
459 root 1.8 our $profile = $cfg;
460 root 1.1
461 root 1.11 sub trace {
462     my ($node) = @_;
463     my $cv = AE::cv;
464     my %seen;
465    
466 root 1.28 my $to = AE::timer 15, 0, sub { exit 1 };
467 root 1.11
468 root 1.25 init;
469 root 1.11
470     my $reply = port {
471 root 1.12 my ($node, @neigh) = @_;
472 root 1.11
473 root 1.12 @neigh = grep $_ ne $NODE, @neigh;
474    
475     print +(join " ", $node, @neigh), "\n";
476    
477     for (@neigh) {
478 root 1.11 unless ($seen{$_}++) {
479     $cv->begin;
480     snd $_, up_nodes => $SELF => $_;
481     }
482     }
483    
484     $cv->end;
485     };
486    
487     $cv->begin;
488     snd $reply, seed => $node;
489    
490     $cv->recv;
491     }
492    
493 root 1.10 sub docmd;
494    
495 root 1.1 our %CMD = (
496     snd => sub {
497 root 1.17 my $port = shift @ARGV;
498 root 1.25 init;
499 root 1.1
500 root 1.7 snd $port, @ARGV; @ARGV = ();
501 root 1.1
502     my $cv = AE::cv;
503 root 1.28 my $to = AE::timer 5, 0, sub { exit 1 };
504 root 1.1 mon $port, $cv;
505 root 1.19 my $reply = port sub { &$cv };
506     snd node_of $port, snd => $reply, "message sent successfully";
507 root 1.1
508     print join " ", $cv->recv, "\n";
509     },
510    
511 root 1.7 rpc => sub {
512 root 1.17 my $port = shift @ARGV;
513 root 1.25 init;
514 root 1.7
515     my $cv = AE::cv;
516 root 1.28 my $to = AE::timer 5, 0, sub { exit 1 };
517 root 1.19 snd $port, @ARGV, port { &$cv }; @ARGV = ();
518 root 1.7 mon $port, $cv;
519    
520 root 1.23 print +(substr JSON::XS->new->encode ([$cv->recv]), 1, -1), "\n";
521 root 1.7 },
522    
523 root 1.1 mon => sub {
524 root 1.17 my $port = shift @ARGV;
525 root 1.25 init;
526 root 1.1
527     mon $port, my $cv = AE::cv;
528     print join " ", $cv->recv, "\n";
529     },
530    
531 root 1.19 eval => sub {
532     my $node = node_of shift @ARGV;
533     my $expr = join " ", @ARGV; @ARGV = ();
534 root 1.25 init;
535 root 1.19
536     my $cv = AE::cv;
537 root 1.28 my $to = AE::timer 5, 0, sub { exit 1 };
538 root 1.19 AnyEvent::MP::Kernel::eval_on $node, $expr, port { &$cv };
539     mon $node, $cv;
540    
541     my ($err, @res) = $cv->recv;
542    
543     die $err if length $err;
544    
545     print +(substr JSON::XS->new->encode (\@res), 1, -1), "\n";
546     },
547    
548 root 1.11 trace => sub {
549     @ARGV >= 1
550 root 1.17 or die "node id missing\n";
551 root 1.11
552 root 1.17 trace shift @ARGV;
553 root 1.11 },
554    
555 root 1.17 setnodeid => sub {
556 root 1.8 @ARGV >= 1
557     or die "shared secret missing\n";
558    
559 root 1.17 $profile->{nodeid} = shift @ARGV;
560 root 1.8 ++$cfg->{dirty};
561     },
562 root 1.17 delnodeid => sub {
563     delete $profile->{nodeid};
564 root 1.8 ++$cfg->{dirty};
565     },
566    
567 root 1.1 setsecret => sub {
568 root 1.8 @ARGV >= 1
569 root 1.1 or die "shared secret missing\n";
570    
571 root 1.8 $profile->{secret} = shift @ARGV;
572 root 1.1 ++$cfg->{dirty};
573     },
574     gensecret => sub {
575 root 1.18 $profile->{secret} = AnyEvent::MP::Kernel::alnumbits AnyEvent::MP::Kernel::nonce 64;
576 root 1.1 ++$cfg->{dirty};
577     },
578 root 1.17 delsecret => sub {
579 root 1.8 delete $profile->{secret};
580 root 1.1 ++$cfg->{dirty};
581     },
582    
583     setcert => sub {
584 root 1.8 @ARGV >= 1
585 root 1.1 or die "key+certificate pem filename missing\n";
586    
587     open my $fh, "<", $ARGV[0]
588     or die "$ARGV[0]: $!";
589    
590     local $/;
591 root 1.8 $profile->{cert} = <$fh>;
592 root 1.1 ++$cfg->{dirty};
593     },
594     gencert => sub {
595 root 1.8 $profile->{cert} = gen_cert;
596 root 1.1 ++$cfg->{dirty};
597     },
598 root 1.17 delcert => sub {
599 root 1.8 delete $profile->{cert};
600 root 1.1 ++$cfg->{dirty};
601     },
602 root 1.6
603 root 1.17 setbinds => sub {
604 root 1.19 @ARGV >= 1
605     or die "bind addresses missing\n";
606     $profile->{binds} = [split /,/, shift @ARGV];
607 root 1.17 ++$cfg->{dirty};
608     },
609     delbinds => sub {
610     delete $profile->{binds};
611     ++$cfg->{dirty};
612     },
613     addbind => sub {
614     @ARGV >= 1
615     or die "bind address missing\n";
616     my $bind = shift @ARGV;
617    
618     @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
619     push @{ $profile->{binds} }, $bind;
620     ++$cfg->{dirty};
621     },
622     delbind => sub {
623     @ARGV >= 1
624     or die "bind address missing\n";
625     my $bind = shift @ARGV;
626    
627     @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
628     ++$cfg->{dirty};
629     },
630    
631 root 1.6 setseeds => sub {
632 root 1.19 @ARGV >= 1
633     or die "seed addresses missing\n";
634     $profile->{seeds} = [split /,/, shift @ARGV];
635 root 1.6 ++$cfg->{dirty};
636     },
637 root 1.17 delseeds => sub {
638     delete $profile->{seeds};
639     ++$cfg->{dirty};
640     },
641 root 1.6 addseed => sub {
642 root 1.10 @ARGV >= 1
643 root 1.17 or die "seed address missing\n";
644 root 1.6 my $seed = shift @ARGV;
645 root 1.10
646 root 1.8 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
647     push @{ $profile->{seeds} }, $seed;
648     ++$cfg->{dirty};
649     },
650     delseed => sub {
651 root 1.10 @ARGV >= 1
652 root 1.17 or die "seed address missing\n";
653 root 1.8 my $seed = shift @ARGV;
654 root 1.10
655 root 1.8 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
656     ++$cfg->{dirty};
657     },
658    
659     setservices => sub {
660 root 1.19 @ARGV >= 1
661     or die "service specifications missing\n";
662     $profile->{services} = [split /,/, shift @ARGV];
663 root 1.6 ++$cfg->{dirty};
664     },
665 root 1.17 delservices => sub {
666     delete $profile->{services};
667     ++$cfg->{dirty};
668     },
669 root 1.9 addservice => sub {
670 root 1.10 @ARGV >= 1
671     or die "service specification missing\n";
672 root 1.8 my $service = shift @ARGV;
673     push @{ $profile->{services} }, $service;
674     ++$cfg->{dirty};
675     },
676 root 1.9 delservice => sub {
677 root 1.10 @ARGV >= 1
678     or die "service specification missing\n";
679 root 1.8 my $service = shift @ARGV;
680     for (0 .. $#{ $profile->{services} }) {
681     next unless $profile->{services}[$_] eq $service;
682     splice @{ $profile->{services} }, $_, 1;
683     last;
684     }
685 root 1.6 ++$cfg->{dirty};
686     },
687 root 1.10
688     profile => sub {
689 root 1.19 @ARGV >= 1
690     or die "profile name is missing\n";
691 root 1.10 my $name = shift @ARGV;
692    
693     $profile = $cfg->{profile}{$name} ||= {};
694 root 1.14 ++$cfg->{dirty};
695 root 1.10 },
696     delprofile => sub {
697     @ARGV >= 1
698     or die "profile name is missing\n";
699     my $name = shift @ARGV;
700    
701     delete $cfg->{profile}{$name};
702 root 1.14 ++$cfg->{dirty};
703 root 1.10 },
704 root 1.26 setparent => sub {
705 root 1.24 @ARGV >= 1
706     or die "profile name is missing\n";
707    
708     $profile->{parent} = shift @ARGV;
709     ++$cfg->{dirty};
710     },
711     delparent => sub {
712     delete $profile->{parent};
713     ++$cfg->{dirty};
714     },
715 root 1.17 showprofile => sub {
716     @ARGV >= 1
717     or die "profile name is missing\n";
718     my $name = shift @ARGV;
719    
720     print JSON::XS->new->pretty->encode ($cfg->{profile}{$name} || {});
721     },
722     showconfig => sub {
723     my $name = @ARGV ? shift @ARGV : AnyEvent::MP::Kernel::_nodename;
724    
725 root 1.25 my $profile = AnyEvent::MP::Config::find_profile $name, @ARGV;
726     @ARGV = ();
727 root 1.24
728     # make it look nicer:
729     delete $profile->{profile};
730     delete $profile->{parent};
731    
732     print JSON::XS->new->pretty->encode ($profile);
733 root 1.17 },
734 root 1.25
735     # undocumented
736     _resolve => sub {
737     print +(join ",", (AnyEvent::MP::Kernel::_resolve shift @ARGV)->recv), "\n";
738     },
739 root 1.1 );
740    
741 root 1.27 for (keys %CMD) {
742     $CMD{$1} = $CMD{$_} if /^set(.*)$/;
743     }
744    
745 root 1.1 sub docmd {
746     my $cmd = shift @ARGV;
747    
748     $CMD{$cmd}
749 root 1.23 or die "$cmd: no such aemp command (try perldoc aemp, or man aemp)";
750 root 1.1
751     $CMD{$cmd}();
752     }
753    
754     @ARGV
755 root 1.23 or die "Usage: aemp subcommand ... (try perldoc aemp, or man aemp)\n";
756 root 1.1
757 root 1.19 docmd while @ARGV;
758 root 1.1
759