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