ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.39
Committed: Mon Sep 7 18:42:09 2009 UTC (14 years, 8 months ago) by root
Branch: MAIN
Changes since 1.38: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

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