File Coverage

lib/ControlFreak/Command.pm
Criterion Covered Total %
statement 107 180 59.4
branch 39 100 39.0
condition 15 44 34.0
subroutine 13 25 52.0
pod 1 5 20.0
total 175 354 49.4


line stmt bran cond sub pod time code
1             package ControlFreak::Command;
2 8     8   156 use strict;
  8         17  
  8         275  
3 8     8   42 use warnings;
  8         13  
  8         277  
4              
5 8     8   4714 use ControlFreak::Service;
  8         152  
  8         117  
6 8     8   5230 use ControlFreak::Console;
  8         31  
  8         94  
7 8     8   5431 use ControlFreak::Socket;
  8         24  
  8         96  
8 8     8   230 use AnyEvent::Socket();
  8         48  
  8         189  
9 8     8   40 use Params::Util qw{ _STRING _INSTANCE _CODE };
  8         17  
  8         506  
10 8     8   780 use Carp;
  8         15  
  8         55474  
11              
12             =encoding utf-8
13              
14             =head1 NAME
15              
16             ControlFreak::Command - turn string commands to method calls
17              
18             =head1 METHODS
19              
20             =head2 process(%param)
21              
22             Process a command from string and call either the C or the C
23             callback with optionally a status string.
24              
25             C<%param> has the following keys
26              
27             =over 4
28              
29             =item * cmd
30              
31             The actual command string to process.
32              
33             =item * ok_cb
34              
35             The callback called when the command executed successfully.
36              
37             =item * err_cb
38              
39             The callback called when the command failed.
40              
41             =item * has_priv
42              
43             A boolean that if set gives access to the entire set of commands.
44              
45             =item * ctrl
46              
47             The controller.
48              
49             =back
50              
51             =cut
52              
53             sub process {
54 40     40 1 26574 my $class = shift;
55 40         239 my %param = @_;
56              
57 40         76 my $cmd = $param{cmd};
58 40   50 0   139 my $ok = $param{ok_cb} || sub {};
  0         0  
59 40   50 0   105 my $err = $param{err_cb} || sub {};
  0         0  
60              
61 40 100       137 if ($cmd) {
62             ## clean
63 36         79 $cmd =~ s/\#.*//; # comments
64 36         96 $cmd =~ s/^\s+//; # leading whitespaces
65 36         126 $cmd =~ s/\s+$//; # trailing whitespaces
66             }
67              
68 40 100       93 if (! $cmd) {
69 7 100       16 if ($param{ignore_void}) {
70 1         4 return;
71             }
72             else {
73 6         18 return $err->("command is void");
74             }
75             }
76              
77 33         125 my ($kw, $rest) = split /\s+/, $cmd, 2;
78              
79 33 50       82 return $err->("empty command") unless $kw;
80              
81 33         76 my $meth = "process_$kw";
82 33         233 my $h = $class->can($meth);
83 33 100       82 return $err->("unknown command '$kw'") unless $h;
84              
85 32         168 return $h->( $class, %param, cmd => $rest );
86             }
87              
88             sub process_service {
89 28     28 0 45 my $class = shift;
90 28         139 my %param = @_;
91              
92 28         52 my $cmd = $param{cmd};
93 28   50 0   87 my $ok = $param{ok_cb} || sub {};
  0         0  
94 28   50 0   70 my $err = $param{err_cb} || sub {};
  0         0  
95 28         45 my $ctrl = $param{ctrl};
96              
97 28 100       66 return $err->("not authorized")
98             unless $param{has_priv};
99              
100 26 100       73 return $err->("empty service command") unless $cmd;
101              
102 23         32 my ($svcname, $attr, $assignment);
103 23 100       137 if ($cmd =~ /^([\w-]+)\s+([\w-]+)\s*=(.*)$/) {
104 17         52 $svcname = $1;
105 17         35 $attr = $2;
106 17         32 $assignment = $3;
107             }
108             else {
109 6         46 return $err->("malformed service command '$cmd'");
110             }
111              
112 17 50       121 my $svc = $ctrl->find_or_create_svc($svcname)
113             or return $err->("service name is invalid");
114              
115             ## Clean the value, before assigning it
116 17         32 my $value = $assignment;
117 17 50       49 if (defined $value) {
118 17         45 $value =~ s/^\s+// ;
119              
120             ## DWIM with quotes
121 17 100 100     106 if ($value =~ /^"(.*)"/ or $value =~ /^'(.*)'/) {
122 2         5 $value = $1;
123             }
124             }
125              
126 17 100 66     98 if (defined $value && ! length $value) {
127 5         8 $value = undef;
128             }
129              
130             ## cmd is special because of the array syntax
131 17 100       46 if ($attr eq 'cmd') {
132 14         57 my $succ = $svc->set_cmd_from_con($value);
133 14 100       55 return $succ ? $ok->($svc) : $err->("invalid value");
134             }
135              
136             ## attributes existence check
137 3         9 my $meth = "set_$attr";
138 3         25 my $h = $svc->can($meth);
139 3 50       24 return $err->("invalid property '$attr'")
140             unless $h;
141              
142 0         0 my $success;
143 0 0       0 if (defined $value) {
144 0 0       0 $value = _as_bool($value) if $attr =~/ ^ ignore_std(out|err)
145             | no_new_session
146             | respawn_on_(fail|stop) $/x;
147 0         0 $success = $h->($svc, $value);
148             }
149             else {
150 0         0 $success = $svc->unset($attr);
151             }
152              
153 0 0       0 return $success ? $ok->($svc) : $err->("invalid value");
154             }
155              
156             sub process_command {
157 1     1 0 3 my $class = shift;
158 1         4 my %param = @_;
159              
160 1         3 my $cmd = $param{cmd};
161 1   50 0   5 my $ok = $param{ok_cb} || sub {};
  0         0  
162 1   50 0   4 my $err = $param{err_cb} || sub {};
  0         0  
163 1         3 my $ctrl = $param{ctrl};
164              
165 1 50       4 return $err->("empty command") unless $cmd;
166              
167 1         4 my ($command, @args) = split /\s+/, $cmd;
168              
169 1 50 33     8 return $err->("malformed service command '$cmd'")
170             unless $command or @args;
171              
172 1         4 my $meth = "command_$command";
173 1         10 my $h = $ctrl->can($meth);
174 1 50       4 return $err->("unknown command '$command'") unless $h;
175 1         11 $h->($ctrl, args => \@args, %param, err_cb => $err, ok_cb => $ok);
176 1         75 return;
177             }
178              
179             ## FIXME: very similar to process_service
180             sub process_socket {
181 0     0 0 0 my $class = shift;
182 0         0 my %param = @_;
183              
184 0         0 my $cmd = $param{cmd};
185 0   0 0   0 my $ok = $param{ok_cb} || sub {};
  0         0  
186 0   0 0   0 my $err = $param{err_cb} || sub {};
  0         0  
187 0         0 my $ctrl = $param{ctrl};
188              
189 0 0       0 return $err->("not authorized")
190             unless $param{has_priv};
191              
192 0 0       0 return $err->("empty socket command") unless $cmd;
193              
194 0         0 my ($sockname, $attr, $assignment);
195 0 0       0 if ($cmd =~ /^([\w-]+)\s+([\w-]+)\s*=(.*)$/) {
196 0         0 $sockname = $1;
197 0         0 $attr = $2;
198 0         0 $assignment = $3;
199             }
200             else {
201 0         0 return $err->("malformed socket command '$cmd'");
202             }
203              
204 0 0       0 my $sock = $ctrl->find_or_create_sock($sockname)
205             or return $err->("socket name is invalid");
206              
207             ## Clean the value, before assigning it
208 0         0 my $value = $assignment;
209 0 0       0 if (defined $value) {
210 0         0 $value =~ s/^\s+// ;
211             ## ugly (and repeated)
212 0 0       0 $value = _as_bool($value) if $attr eq 'nonblocking';
213              
214             ## DWIM with quotes
215 0 0 0     0 if ($value =~ /^"(.*)"/ or $value =~ /^'(.*)'/) {
216 0         0 $value = $1;
217             }
218             }
219              
220 0 0 0     0 if (defined $value && ! length $value) {
221 0         0 $value = undef;
222             }
223              
224             ## attributes existence check
225 0         0 my $meth = "set_$attr";
226 0         0 my $h = $sock->can($meth);
227 0 0       0 return $err->("invalid property '$attr'")
228             unless $h;
229              
230 0         0 my $success;
231 0 0       0 if (defined $value) {
232 0         0 $success = $h->($sock, $value);
233             }
234             else {
235 0         0 $success = $sock->unset($attr);
236             }
237              
238 0 0       0 return $success ? $ok->($sock) : $err->("invalid value");
239             }
240              
241             sub process_proxy {
242 5     5 0 9 my $class = shift;
243 5         17 my %param = @_;
244              
245 5         12 my $cmd = $param{cmd};
246 5   50 0   14 my $ok = $param{ok_cb} || sub {};
  0            
247 5   50 0   15 my $err = $param{err_cb} || sub {};
  0            
248 5         9 my $ctrl = $param{ctrl};
249              
250 5 100       12 return $err->("not authorized")
251             unless $param{has_priv};
252              
253 4 50       10 return $err->("empty proxy command") unless $cmd;
254              
255 4         5 my ($proxyname, $subcmd, $rest, $attr, $assignment);
256 4 100       29 if ($cmd =~ /^([\w-]+)\s+([\w-]+)\s+(.+)$/) {
    50          
257 2         6 $proxyname = $1;
258 2         10 $subcmd = $2;
259 2         9 $rest = $3;
260             }
261             elsif ($cmd =~ /^([\w-]+)\s+([\w-]+)\s*=(.*)$/) {
262 0         0 $proxyname = $1;
263 0         0 $attr = $2;
264 0         0 $assignment = $3;
265             }
266             else {
267 2         8 return $err->("malformed proxy command '$cmd'");
268             }
269              
270 2 50       20 my $proxy = $ctrl->find_or_create_proxy($proxyname)
271             or return $err->("proxy name is invalid");
272              
273 2 50 33     21 if ($subcmd && $subcmd eq 'service') {
274 2         4 my $svc;
275             $class->process_service(
276             cmd => $rest,
277             ctrl => $ctrl,
278             has_priv => 1,
279 1     1   7 ok_cb => sub { $svc = $_[0] },
280 2         376 err_cb => $err,
281             );
282 2 100       18 return unless $svc;
283 1         8 $proxy->add_service($svc);
284 1         4 return $ok->($proxy);
285             }
286              
287             ## cmd is special because of the array syntax
288 0 0         if ($attr eq 'cmd') {
289 0           my $succ = $proxy->set_cmd_from_con($assignment);
290 0 0         return $succ ? $ok->($proxy) : $err->("invalid value");
291             }
292              
293             ## Clean the value, before assigning it
294 0           my $value = $assignment;
295 0 0         if (defined $value) {
296 0           $value =~ s/^\s+// ;
297 0 0         $value = _as_bool($value) if $attr =~ / ^noauto /x;
298              
299             ## DWIM with quotes
300 0 0 0       if ($value =~ /^"(.*)"/ or $value =~ /^'(.*)'/) {
301 0           $value = $1;
302             }
303             }
304              
305 0 0 0       if (defined $value && ! length $value) {
306 0           $value = undef;
307             }
308              
309             ## attributes existence check
310 0           my $meth = "set_$attr";
311 0           my $h = $proxy->can($meth);
312 0 0         return $err->("invalid property '$attr'")
313             unless $h;
314              
315 0           my $success;
316 0 0         if (defined $value) {
317 0           $success = $h->($proxy, $value);
318             }
319             else {
320 0           $success = $proxy->unset($attr);
321             }
322              
323 0 0         return $success ? $ok->($proxy) : $err->("invalid value");
324             }
325              
326             sub _as_bool {
327 0     0     my $value = shift;
328 0 0         return 1 if $value =~ /^1| true| on| enabled|yes/xi;
329 0 0         return 0 if $value =~ /^0|false|off|disabled| no/xi;
330 0           return;
331              
332             }
333              
334             "cd&c";