ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.41
Committed: Fri Sep 11 02:32:23 2009 UTC (14 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-1_1
Changes since 1.40: +9 -11 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 root 1.41 aemp cal <port> <arg...> # send message, append reply
15 root 1.19 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 root 1.41 =item cal <port> <arg...>
160 root 1.23
161 root 1.41 Like F<aemp cal>: appends a local reply port to the message and waits for
162     a message to it.
163 root 1.23
164     Any return values will be JSON-encoded and printed separated by commas
165     (kind of like a JSON array without []-brackets).
166    
167 root 1.41 Example: ask the (undocumented) time service of a node for its current
168 root 1.23 time.
169    
170 root 1.41 aemp cal mynode time
171 root 1.23
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 root 1.41 node. Return values are handled as with F<aemp cal>.
185 root 1.23
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.41 cal => 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.41 cal $port, @ARGV, sub { &$cv }; @ARGV = ();
592 root 1.7
593 root 1.23 print +(substr JSON::XS->new->encode ([$cv->recv]), 1, -1), "\n";
594 root 1.7 },
595    
596 root 1.1 mon => sub {
597 root 1.17 my $port = shift @ARGV;
598 root 1.25 init;
599 root 1.1
600     mon $port, my $cv = AE::cv;
601     print join " ", $cv->recv, "\n";
602     },
603    
604 root 1.19 eval => sub {
605     my $node = node_of shift @ARGV;
606     my $expr = join " ", @ARGV; @ARGV = ();
607 root 1.37 node_eval $node, $expr;
608 root 1.19 },
609    
610 root 1.11 trace => sub {
611     @ARGV >= 1
612 root 1.17 or die "node id missing\n";
613 root 1.11
614 root 1.17 trace shift @ARGV;
615 root 1.11 },
616 root 1.37 restart => sub {
617     my $node = node_of shift @ARGV;
618 root 1.40 node_eval $node, 'my $w; $w = AE::idle sub { undef $w; AnyEvent::Watchdog::Util::restart }; ()';
619 root 1.37 },
620 root 1.11
621 root 1.17 setnodeid => sub {
622 root 1.8 @ARGV >= 1
623     or die "shared secret missing\n";
624    
625 root 1.17 $profile->{nodeid} = shift @ARGV;
626 root 1.8 ++$cfg->{dirty};
627     },
628 root 1.17 delnodeid => sub {
629     delete $profile->{nodeid};
630 root 1.8 ++$cfg->{dirty};
631     },
632    
633 root 1.1 setsecret => sub {
634 root 1.8 @ARGV >= 1
635 root 1.1 or die "shared secret missing\n";
636    
637 root 1.8 $profile->{secret} = shift @ARGV;
638 root 1.1 ++$cfg->{dirty};
639     },
640     gensecret => sub {
641 root 1.18 $profile->{secret} = AnyEvent::MP::Kernel::alnumbits AnyEvent::MP::Kernel::nonce 64;
642 root 1.1 ++$cfg->{dirty};
643     },
644 root 1.17 delsecret => sub {
645 root 1.8 delete $profile->{secret};
646 root 1.1 ++$cfg->{dirty};
647     },
648    
649     setcert => sub {
650 root 1.8 @ARGV >= 1
651 root 1.1 or die "key+certificate pem filename missing\n";
652    
653     open my $fh, "<", $ARGV[0]
654     or die "$ARGV[0]: $!";
655    
656     local $/;
657 root 1.8 $profile->{cert} = <$fh>;
658 root 1.1 ++$cfg->{dirty};
659     },
660     gencert => sub {
661 root 1.8 $profile->{cert} = gen_cert;
662 root 1.1 ++$cfg->{dirty};
663     },
664 root 1.17 delcert => sub {
665 root 1.8 delete $profile->{cert};
666 root 1.1 ++$cfg->{dirty};
667     },
668 root 1.6
669 root 1.17 setbinds => sub {
670 root 1.19 @ARGV >= 1
671     or die "bind addresses missing\n";
672 root 1.38 my $list = shift @ARGV;
673     $profile->{binds} = ref $list ? $list : [split /,/, $list];
674 root 1.17 ++$cfg->{dirty};
675     },
676     delbinds => sub {
677     delete $profile->{binds};
678     ++$cfg->{dirty};
679     },
680     addbind => sub {
681     @ARGV >= 1
682     or die "bind address missing\n";
683     my $bind = shift @ARGV;
684    
685     @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
686     push @{ $profile->{binds} }, $bind;
687     ++$cfg->{dirty};
688     },
689     delbind => sub {
690     @ARGV >= 1
691     or die "bind address missing\n";
692     my $bind = shift @ARGV;
693    
694     @{ $profile->{binds} } = grep $_ ne $bind, @{ $profile->{binds} };
695     ++$cfg->{dirty};
696     },
697    
698 root 1.6 setseeds => sub {
699 root 1.19 @ARGV >= 1
700     or die "seed addresses missing\n";
701 root 1.38 my $list = shift @ARGV;
702     $profile->{seeds} = ref $list ? $list : [split /,/, $list];
703 root 1.6 ++$cfg->{dirty};
704     },
705 root 1.17 delseeds => sub {
706     delete $profile->{seeds};
707     ++$cfg->{dirty};
708     },
709 root 1.6 addseed => sub {
710 root 1.10 @ARGV >= 1
711 root 1.17 or die "seed address missing\n";
712 root 1.6 my $seed = shift @ARGV;
713 root 1.10
714 root 1.8 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
715     push @{ $profile->{seeds} }, $seed;
716     ++$cfg->{dirty};
717     },
718     delseed => sub {
719 root 1.10 @ARGV >= 1
720 root 1.17 or die "seed address missing\n";
721 root 1.8 my $seed = shift @ARGV;
722 root 1.10
723 root 1.8 @{ $profile->{seeds} } = grep $_ ne $seed, @{ $profile->{seeds} };
724     ++$cfg->{dirty};
725     },
726    
727     setservices => sub {
728 root 1.19 @ARGV >= 1
729     or die "service specifications missing\n";
730 root 1.38 my $list = shift @ARGV;
731     $profile->{services} = ref $list ? $list : [split /,/, $list];
732 root 1.6 ++$cfg->{dirty};
733     },
734 root 1.17 delservices => sub {
735     delete $profile->{services};
736     ++$cfg->{dirty};
737     },
738 root 1.9 addservice => sub {
739 root 1.10 @ARGV >= 1
740     or die "service specification missing\n";
741 root 1.8 my $service = shift @ARGV;
742     push @{ $profile->{services} }, $service;
743     ++$cfg->{dirty};
744     },
745 root 1.9 delservice => sub {
746 root 1.10 @ARGV >= 1
747     or die "service specification missing\n";
748 root 1.8 my $service = shift @ARGV;
749     for (0 .. $#{ $profile->{services} }) {
750     next unless $profile->{services}[$_] eq $service;
751     splice @{ $profile->{services} }, $_, 1;
752     last;
753     }
754 root 1.6 ++$cfg->{dirty};
755     },
756 root 1.10
757     profile => sub {
758 root 1.19 @ARGV >= 1
759     or die "profile name is missing\n";
760 root 1.10 my $name = shift @ARGV;
761    
762     $profile = $cfg->{profile}{$name} ||= {};
763 root 1.14 ++$cfg->{dirty};
764 root 1.10 },
765     delprofile => sub {
766     @ARGV >= 1
767     or die "profile name is missing\n";
768     my $name = shift @ARGV;
769    
770     delete $cfg->{profile}{$name};
771 root 1.14 ++$cfg->{dirty};
772 root 1.10 },
773 root 1.26 setparent => sub {
774 root 1.24 @ARGV >= 1
775     or die "profile name is missing\n";
776    
777     $profile->{parent} = shift @ARGV;
778     ++$cfg->{dirty};
779     },
780     delparent => sub {
781     delete $profile->{parent};
782     ++$cfg->{dirty};
783     },
784 root 1.17 showprofile => sub {
785     @ARGV >= 1
786     or die "profile name is missing\n";
787     my $name = shift @ARGV;
788    
789     print JSON::XS->new->pretty->encode ($cfg->{profile}{$name} || {});
790     },
791     showconfig => sub {
792     my $name = @ARGV ? shift @ARGV : AnyEvent::MP::Kernel::_nodename;
793    
794 root 1.25 my $profile = AnyEvent::MP::Config::find_profile $name, @ARGV;
795     @ARGV = ();
796 root 1.24
797     # make it look nicer:
798     delete $profile->{profile};
799     delete $profile->{parent};
800    
801     print JSON::XS->new->pretty->encode ($profile);
802 root 1.17 },
803 root 1.25
804     # undocumented
805     _resolve => sub {
806     print +(join ",", (AnyEvent::MP::Kernel::_resolve shift @ARGV)->recv), "\n";
807     },
808 root 1.1 );
809    
810 root 1.27 for (keys %CMD) {
811     $CMD{$1} = $CMD{$_} if /^set(.*)$/;
812     }
813    
814 root 1.1 sub docmd {
815     my $cmd = shift @ARGV;
816    
817     $CMD{$cmd}
818 root 1.23 or die "$cmd: no such aemp command (try perldoc aemp, or man aemp)";
819 root 1.1
820     $CMD{$cmd}();
821     }
822    
823     @ARGV
824 root 1.23 or die "Usage: aemp subcommand ... (try perldoc aemp, or man aemp)\n";
825 root 1.1
826 root 1.19 docmd while @ARGV;
827 root 1.1
828