ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.45
Committed: Thu Nov 26 09:03:23 2009 UTC (14 years, 6 months ago) by elmex
Branch: MAIN
Changes since 1.44: +4 -2 lines
Log Message:
fixed small bug in setcert.

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