ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/bin/aemp
Revision: 1.36
Committed: Fri Sep 4 21:46:10 2009 UTC (14 years, 9 months ago) by root
Branch: MAIN
Changes since 1.35: +8 -7 lines
Log Message:
*** empty log message ***

File Contents

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