File Coverage

lib/ControlFreak/Service.pm
Criterion Covered Total %
statement 363 437 83.0
branch 97 166 58.4
condition 36 55 65.4
subroutine 70 87 80.4
pod 28 48 58.3
total 594 793 74.9


line stmt bran cond sub pod time code
1             package ControlFreak::Service;
2 8     8   44 use strict;
  8         17  
  8         319  
3 8     8   40 use warnings;
  8         13  
  8         233  
4              
5 8     8   14264 use AnyEvent '5.202';
  8         55456  
  8         295  
6 8     8   7944 use AnyEvent::Util();
  8         139046  
  8         233  
7 8     8   9991 use AnyEvent::Handle();
  8         81792  
  8         243  
8 8     8   97 use Carp;
  8         16  
  8         714  
9 8     8   5020 use ControlFreak::Util();
  8         28  
  8         193  
10 8     8   8938 use Data::Dumper();
  8         78213  
  8         220  
11 8     8   9865 use JSON::XS;
  8         61271  
  8         12280  
12 8     8   16065 use Params::Util qw{ _NUMBER _STRING _IDENTIFIER _ARRAY _POSINT };
  8         32692  
  8         961  
13 8     8   70 use POSIX qw{ SIGTERM SIGKILL };
  8         15  
  8         84  
14 8     8   26615 use Try::Tiny;
  8         21830  
  8         533  
15              
16 8     8   58 use constant DEFAULT_STARTWAIT_SECS => 1;
  8         16  
  8         351  
17 8     8   42 use constant DEFAULT_STOPWAIT_SECS => 2;
  8         16  
  8         311  
18 8     8   43 use constant DEFAULT_MAX_RETRIES => 8;
  8         17  
  8         306  
19 8     8   37 use constant BASE_BACKOFF_DELAY => 0.3;
  8         18  
  8         554  
20              
21 8         79 use Object::Tiny qw{
22             name
23             desc
24             proxy
25              
26             state
27             pid
28             start_time
29             stop_time
30             running_cmd
31              
32             cmd
33             env
34             cwd
35             tags
36             tie_stdin_to
37             ignore_stderr
38             ignore_stdout
39             startwait_secs
40             stopwait_secs
41             respawn_on_fail
42             respawn_on_stop
43             respawn_max_retries
44             no_new_session
45             user
46             group
47             priority
48 8     8   45 };
  8         15  
49              
50             =pod
51              
52             =head1 NAME
53              
54             ControlFreak::Service - Object representation of a service.
55              
56             =head1 SYNOPSIS
57              
58             my $mc = ControlFreak::Service->new(
59             name => "memcached",
60             desc => "you should have this one...",
61             ignore_stderr => 1,
62             cmd => "/usr/bin/memcached",
63             );
64              
65             my $fcgisock = $ctrl->socketmap->{fcgi};
66             my $web = ControlFreak::Service->new(
67             name => "fcgi",
68             desc => "I talk http",
69             tie_stdin_to => $fcgisock,
70             cmd => "/usr/bin/plackup -a MyApp -s FCGI",
71             );
72             $web->up;
73             $web->start;
74             $web->stop;
75              
76             ## A service can mutate
77             $web->add_tag('prod');
78              
79             ## all set_* accessors are callable from Commands
80             $web->set_cmd("/usr/bin/plackup -a MyNewApp");
81             $web->set_ignore_stderr(0);
82             # ...
83              
84             $web->running_cmd;
85              
86             # make the service a proxied service
87             $web->assign_proxy($proxy);
88              
89             =head1 DESCRIPTION
90              
91             This allows manipulation of a service and its state.
92              
93             =head1 METHODS
94              
95             =head2 new(%param)
96              
97             constructor.
98              
99             =cut
100              
101             sub new {
102 26     26 1 1038 my $svc = shift->SUPER::new(@_);
103 26         839 my %param = @_;
104              
105             ## validate the service name
106 26 100       955 unless (_IDENTIFIER($svc->name)) {
107 2         170 return;
108             }
109              
110             ## sensible default
111 24 50       1618 $svc->{respawn_on_fail} = 1
112             unless exists $svc->{respawn_on_fail};
113              
114 24 50       166 $svc->{respawn_max_retries} = DEFAULT_MAX_RETRIES
115             unless defined $svc->{respawn_max_retries};
116              
117 24 100       126 $svc->{startwait_secs} = DEFAULT_STARTWAIT_SECS
118             unless defined $svc->{startwait_secs};
119              
120 24 50       125 $svc->{stopwait_secs} = DEFAULT_STOPWAIT_SECS
121             unless defined $svc->{stopwait_secs};
122              
123 24 50       124 $svc->{ctrl} = $param{ctrl}
124             or croak "Service requires a controller";
125              
126 24   50     365 $svc->{tags} ||= {};
127 24   50     583 $svc->{env} ||= {};
128              
129 24         109 return $svc;
130             }
131              
132 2     2   15 sub _err { ControlFreak::Util::error(@_) }
133              
134             =head2 is_fail
135              
136             Returns true if the state is 'failed'
137              
138             =cut
139              
140             sub is_fail {
141 26   50 26 1 355057 my $state = shift->state || "";
142 26         429 return $state eq 'fail';
143             }
144              
145             =head2 is_backoff
146              
147             Returns true if the state is 'backoff'
148              
149             =cut
150              
151             sub is_backoff {
152 79   100 79 1 637399 my $state = shift->state || "";
153 79         1566 return $state eq 'backoff';
154             }
155              
156             =head2 is_fatal
157              
158             Returns true if the state is 'fatal'
159              
160             =cut
161              
162             sub is_fatal {
163 15   50 15 1 619273 my $state = shift->state || "";
164 15         194 return $state eq 'fatal';
165             }
166              
167             =head2 is_running
168              
169             Returns true if the state is 'runnnig'
170              
171             =cut
172              
173             sub is_running {
174 53   50 53 1 1675372 my $state = shift->state || "";
175 53         983 return $state eq 'running';
176             }
177              
178             =head2 is_starting
179              
180             Returns true if the state is 'starting'
181              
182             =cut
183              
184             sub is_starting {
185 61   50 61 1 625338 my $state = shift->state || "";
186 61         1060 return $state eq 'starting';
187             }
188              
189             =head2 is_stopping
190              
191             Returns true if the state is 'stopping'
192              
193             =cut
194              
195             sub is_stopping {
196 3   50 3 1 1983 my $state = shift->state || "";
197 3         41 return $state eq 'stopping';
198             }
199              
200             =head2 is_stopped
201              
202             Returns true is service is stopped
203              
204             =cut
205              
206             sub is_stopped {
207 53   50 53 1 646132 my $state = shift->state || "";
208 53         757 return $state eq 'stopped';
209             }
210              
211             =head2 is_up
212              
213             Returns true is service is up
214              
215             =cut
216              
217             sub is_up {
218 78     78 1 3167 my $svc = shift;
219 78   100     2456 my $state = $svc->state || "";
220 78 100       1313 return 0 unless $state =~ /^(?:running|starting|stopping)$/;
221              
222 21 100       107 unless ($svc->{proxy}) {
223             ## just in case, verify...
224 19 50       92 return 0 unless defined $svc->{child_cv};
225 19 50       668 return 0 unless defined $svc->pid;
226             }
227 21         207 return 1;
228             }
229              
230             =head2 is_down
231              
232             Returns true unless service is up
233              
234             =cut
235              
236             sub is_down {
237 35     35 1 122892 return !shift->is_up;
238             }
239              
240             =head2 fail_reason
241              
242             Returns a string with the reason of the failure, or undef.
243              
244             =cut
245              
246             sub fail_reason {
247 7     7 1 1116 my $svc = shift;
248 7 100       30 return unless $svc->is_fail;
249 6         41 return ControlFreak::Util::exit_reason( $svc->{exit_status} );
250             }
251              
252             =head2 stop(%param)
253              
254             Initiates service shutdown.
255              
256             params are:
257              
258             =over 4
259              
260             =item * ok_cb
261              
262             A callback called when shutdown has been initiated successfuly. Note that it
263             doesn't mean that the service is successfuly stopped, just that nothing
264             prevented the shutdown sequence.
265              
266             Optional.
267              
268             =item * err_cb
269              
270             Called with a text reason when the stop request couldn't be initiated properly.
271              
272             Optional.
273              
274             =back
275              
276             =cut
277              
278             sub stop {
279 15     15 1 2079 my $svc = shift;
280 15         66 my %param = @_;
281 15   100 0   282 my $err = $param{err_cb} ||= sub {};
  0         0  
282 15   100 11   564 my $ok = $param{ok_cb} ||= sub {};
  11         22  
283              
284 15   50     851 my $svcname = $svc->name || "unnamed service";
285              
286 15 100       275 if ($svc->is_backoff) {
287             ## stop retrying.
288 2         13 $svc->{backoff_cv} = undef;
289 2         29 $svc->{start_cv} = undef;
290 2         13 $svc->{backoff_retry} = undef;
291 2         14 $svc->{state} = 'stopped';
292 2         28 $svc->{wants_down} = 1;
293 2         14 $svc->{stop_time} = time;
294 2         17 return;
295             }
296              
297 13 100       65 return $svc->_err(%param, "Service '$svcname' is already down")
298             if $svc->is_down;
299              
300 12 50       342 return $svc->_err(%param, "Service '$svcname' lost its pid")
301             unless $svc->pid;
302              
303 12         525 $svc->{ctrl}->log->info("Stopping service '$svcname'");
304              
305             ## there is a slight race condition here, since the child
306             ## might have died just before we send the TERM signal, but
307             ## we trust the kernel not to reallocate the pid in the meantime
308 12         1565 $svc->{stop_time} = time;
309 12         35 $svc->{start_time} = undef;
310 12         34 $svc->{state} = 'stopping';
311 12         35 $svc->{wants_down} = 1;
312              
313 12         162 $svc->{on_stop_cb} = $param{on_stop};
314 12         400 my $stopwait_secs = $svc->stopwait_secs;
315             $svc->{stop_cv} =
316 12     1   199 AE::timer $stopwait_secs, 0, sub { $svc->_check_stopping_state };
  1         44335  
317              
318 12 100       221 if (my $proxy = $svc->{proxy}) {
319 1         15 $proxy->stop_service(%param, service => $svc);
320             }
321             else {
322 11         318 my $pid = $svc->pid;
323 11 50       83 if (! $pid) {
324 0         0 my $msg = "Please retry in a bit, no pid yet";
325 0 0       0 if (! $svc->is_starting) {
326 0         0 $msg = "Something weird is going on. pid missing";
327             }
328 0         0 return $svc->_err(%param, $msg);
329             }
330             else {
331             ## check that we've created a session
332 11         306 my $has_new_session = !$svc->no_new_session;
333 11 50       78 if ($has_new_session) {
334 11 50       158 if (getpgrp($pid) == getpgrp(0)) {
335             ## don't commit suicide, thank you.
336 0         0 kill SIGTERM, $pid;
337             }
338             else {
339 11         623 kill -(SIGTERM), getpgrp($pid);
340             }
341             }
342             else {
343             ## ok, only kill that one pid
344 0         0 kill SIGTERM, $pid;
345             }
346             }
347             }
348 12         130 $ok->();
349 12         499 return 1;
350             }
351              
352             =head2 start(%param)
353              
354             Initiates service startup and returns immediately.
355              
356             params are:
357              
358             =over 4
359              
360             =item * ok_cb
361              
362             A callback called when startup has been initiated successfuly. Note that it
363             doesn't mean that the service is successfuly running, just that nothing
364             prevented the startup.
365              
366             Optional.
367              
368             =item * err_cb
369              
370             A callback called when an error occured during startup (For instance
371             if the service is already started), the reason for the failure is
372             passed as the first argument of the callback if is known.
373              
374             Optional.
375              
376             =back
377              
378             =cut
379              
380             sub start {
381 35     35 1 3511 my $svc = shift;
382 35         125 my %param = @_;
383 35   100 0   568 my $err = $param{err_cb} ||= sub {};
  0         0  
384 35   100 32   427 my $ok = $param{ok_cb} ||= sub {};
  32         186  
385              
386 35   50     1315 my $svcname = $svc->name || "unnamed service";
387              
388 35 50       659 return $svc->_err(%param, "Service '$svcname' is already up")
389             if $svc->is_up;
390              
391 35         984 my $cmd = $svc->cmd;
392 35 100       925 return $svc->_err(%param, "Service '$svcname' has no known command")
393             unless $cmd;
394              
395 34         163 $svc->{restart_cv} = undef;
396 34         185 $svc->{start_time} = time;
397 34         158 $svc->{stop_time} = undef;
398 34         94 $svc->{wants_down} = undef;
399 34         95 $svc->{normal_exit} = undef;
400 34 100       203 $svc->{backoff_retry} = undef unless $svc->is_backoff;
401 34         223 $svc->{state} = 'starting';
402              
403 34         175 $svc->set_check_running_state_timer;
404 34 100       148 if (my $proxy = $svc->{proxy}) {
405 5         64 $proxy->start_service(%param, service => $svc);
406             }
407             else {
408 29         132 $svc->_run_cmd;
409             }
410              
411 34         1135 $ok->();
412 34         6712 return 1;
413             }
414              
415             ## set a timer to verify service is up, the timer can be reset
416             ## a second time by the proxy when it gets the pid, it depends
417             ## which event happens first
418             sub set_check_running_state_timer {
419 38     38 0 76 my $svc = shift;
420 38         1190 my $startwait_secs = $svc->startwait_secs;
421 38         1522 $svc->{ctrl}->log->debug("setting timer for $startwait_secs");
422             $svc->{start_cv} =
423 38     12   4575 AE::timer $startwait_secs, 0, sub { $svc->_check_running_state };
  12         52999  
424 38         26686 return;
425             }
426              
427             =head2 has_stopped($reason)
428              
429             Called when a third party knows that a service has stopped. It marks the
430             service has stopped, no matter what the current status is.
431              
432             =cut
433              
434             ## FIXME name
435             sub has_stopped {
436 3     3 1 8 my $svc = shift;
437 3   50     33 my $reason = shift || "";
438 3 100       172 return if $svc->is_down;
439              
440 1         28 my $name = $svc->name;
441 1         7 $reason = "'$name' has stopped: $reason";
442 1         9 $svc->{state} = 'fail';
443 1         6 $svc->{stop_time} = time;
444 1         3 $svc->{normal_exit} = undef;
445 1         6 $svc->{child_cv} = undef;
446 1         28 $svc->{ctrl}->log->info($reason);
447 1         59 return 1;
448             }
449              
450             sub _check_stopping_state {
451 1     1   11 my $svc = shift;
452 1         13 my $on_stop = $svc->{on_stop_cb};
453 1         8 $svc->{stop_cv} = undef;
454 1         4 $svc->{on_stop_cb} = undef;
455 1 50       7 if ($svc->is_stopped) {
456 0 0       0 $on_stop->($svc) if $on_stop;
457 0         0 return;
458             }
459              
460 1         30 my $wait = $svc->stopwait_secs;
461 1         48 my $name = $svc->name;
462 1 50       37 if ($svc->pid) {
463 1         39 $svc->{ctrl}->log->warn(
464             "service $name still running after $wait, killing."
465             );
466 1         131 $svc->kill;
467             }
468             else {
469 0         0 $svc->{ctrl}->log->error( "service $name not stopped but, not pid?");
470 0         0 $svc->{state} = 'fail';
471             }
472 1         17 return;
473             }
474              
475             =head2 kill
476              
477             Kills the service. This is the brutal way of getting rid of service's process
478             it will result in the program being uncleanly exited which will be reported
479             later in the status of the service. This command is used when a service
480             hasn't terminated after C.
481              
482             =cut
483              
484             sub kill {
485 1     1 1 3 my $svc = shift;
486 1         26 my $pid = $svc->pid;
487 1 50       8 unless ($pid) {
488 0         0 my $name = $svc->name;
489 0         0 $svc->{ctrl}->log->error( "cannot kill $name without pid" );
490             }
491 1         29 kill -(SIGKILL), getpgrp($pid);
492             }
493              
494             sub _check_running_state {
495 12     12   62 my $svc = shift;
496 12         59 $svc->{start_cv} = undef;
497 12         708 $svc->{ctrl}->log->debug("state is " . $svc->state);
498 12 50       2796 return unless $svc->is_starting;
499 12 50       433 if (! $svc->pid) {
500 0 0       0 if (my $proxy = $svc->{proxy}) {
501 0         0 $svc->{ctrl}->log->warn(
502             "increase startwait_secs, proxy didn't have time to start svc"
503             );
504 0         0 return;
505             }
506 0         0 $svc->{ctrl}->log->error("smth went terribly wrong");
507 0         0 $svc->{state} = 'fail';
508 0         0 return;
509             }
510 12         454 my $name = $svc->name;
511 12         453 $svc->{ctrl}->log->debug("Now setting '$name' service as running");
512 12         1121 $svc->{state} = 'running';
513 12         393 $svc->{backoff_retry} = undef;
514             }
515              
516             sub _backoff_restart {
517 4     4   18 my $svc = shift;
518 4         79 $svc->{backoff_cv} = undef;
519 4 50       36 return unless $svc->is_backoff;
520 4         35 my $n = $svc->{backoff_retry} + 1;
521 4         127 my $s = $svc->name;
522 4         6149 $svc->{ctrl}->log->info("restarting $s [attempt: $n]");
523 4         567 $svc->start;
524 4         262 return;
525             }
526              
527             sub _exponential_backoff_delay {
528 7     7   26 my $svc = shift;
529 7   50     25 my $retry = shift || 1;
530 7         200 my $max_retries = $svc->respawn_max_retries;
531 7         87 my $factor = int(rand (2 * $retry - 1) + 1);
532 7         69 return $factor * BASE_BACKOFF_DELAY;
533             }
534              
535             =head2 up(%param)
536              
537             XXX up the service (do nothing if already up)
538              
539             =cut
540              
541             sub up {
542 1     1 1 8 my $svc = shift;
543 1 50       4 return if $svc->is_up;
544 1         8 return $svc->start(@_);
545             }
546              
547             =head2 up(%param)
548              
549             XXX down the service (do nothing if already down)
550              
551             =cut
552              
553             sub down {
554 2     2 1 5 my $svc = shift;
555 2         13 my %param = @_;
556 2 100       11 if ($svc->is_down) {
557 1 50       7 $param{on_stop}->() if $param{on_stop};
558 1         4 return;
559             }
560 1         122 return $svc->stop(@_);
561             }
562              
563             =head2 restart(%param)
564              
565             Restarts the service. i.e. stops it (if up), then starts it.
566              
567             =cut
568              
569             sub restart {
570 2     2 1 1045 my $svc = shift;
571 2         12 my %param = @_;
572 2   50 0   14 my $err = $param{err_cb} ||= sub {};
  0         0  
573 2   50 0   11 my $ok = $param{ok_cb} ||= sub {};
  0         0  
574 2         5 my $fail = 0;
575 2     0   26 $svc->down(%param, ok_cb => $ok, err_cb => sub { $fail++ });
  0         0  
576 2 50       14 return $err->() if $fail;
577 2   50     58 my $stopwait_secs = $svc->stopwait_secs || DEFAULT_STARTWAIT_SECS;
578 2         16 my $delay = $stopwait_secs / 10;
579 2         5 my $tries = 0;
580             $svc->{restart_cv} = AE::timer 0.15, $delay, sub {
581 1     1   9 $tries++;
582 1 50       17 if ($tries > 150) {
583 0         0 $err->();
584 0         0 return;
585             }
586 1 50       7 return if $svc->is_up;
587 1         6 $svc->{restart_cv} = undef;
588 1         12 return $svc->up(%param);
589 2         32 };
590 2         15 return;
591             }
592              
593             =head2 proxy_as_text
594              
595             A descriptive text representing service's proxy.
596              
597             =cut
598              
599             sub proxy_as_text {
600 0     0 1 0 my $svc = shift;
601 0         0 my $proxy = $svc->{proxy};
602 0 0       0 return "" unless $proxy;
603 0   0     0 my $name = $proxy->name || "";
604 0 0       0 my $status = $proxy->is_running ? "" : "!";
605 0         0 return "$name$status";
606             }
607              
608             =head2 status_as_text
609              
610             Returns a text describing the service state.
611             It consists in tab separated list of fields:
612              
613             =over 2
614              
615             =item * name
616              
617             =item * state
618              
619             =item * pid
620              
621             =item * start_time
622              
623             =item * stop_time
624              
625             =item * proxy, prefixed with '!' if down
626              
627             =item * fail_reason
628              
629             =item * running_cmd
630              
631             =back
632              
633             =cut
634              
635             sub status_as_text {
636 0     0 1 0 my $svc = shift;
637 0 0       0 return join "\t", map { $svc->$_ || "" }
  0         0  
638             qw/name state pid start_time stop_time proxy_as_text
639             fail_reason running_cmd/;
640             }
641              
642             =head2 desc_as_text
643              
644             Returns a text describing the service and how to access it.
645             It consists in tab separated list of fields:
646              
647             =over 2
648              
649             =item * name
650              
651             =item * tags
652              
653             =item * desc
654              
655             =item * proxy
656              
657             =item * cmd
658              
659             =back
660              
661             =cut
662              
663             sub desc_as_text {
664 0     0 1 0 my $svc = shift;
665 0 0       0 return join "\t", map { $svc->$_ || "" }
  0         0  
666             qw/name tags_as_text desc proxy_as_text cmd/;
667             }
668              
669             =head2 assign_proxy($proxy)
670              
671             =cut
672              
673             sub assign_proxy {
674 3     3 1 7 my $svc = shift;
675 3         9 $svc->{proxy} = shift;
676 3         8 return 1;
677             }
678              
679             =head2 assign_pid($pid)
680              
681             =cut
682              
683             sub assign_pid {
684 4     4 1 14 my $svc = shift;
685 4         12 my $pid = shift;
686 4         14 $svc->{pid} = $pid;
687 4         26 return;
688             }
689              
690             sub _set {
691 56     56   104 my $svc = shift;
692 56         854 my ($attr, $value) = @_;
693              
694 56         2144 my $old = $svc->$attr;
695              
696 56 50       405 my $v = defined $value ? $value : "~";
697 56         235 local $Data::Dumper::Indent = 0;
698 56         224 local $Data::Dumper::Terse = 1;
699 56 100       180 if (ref $v) {
700 9         96 $v = Data::Dumper::Dumper($v);
701             }
702 56 100       1507 if ($old) {
703 30 50       114 my $oldv = defined $old ? $old : "~";
704 30 100       106 $oldv = Data::Dumper::Dumper($oldv) if ref $oldv;
705 30         1143 $svc->{ctrl}->log->debug( "Changing $attr from '$oldv' to '$v'" );
706             }
707             else {
708 26         865 $svc->{ctrl}->log->debug( "Setting $attr to '$v'" );
709             }
710 56         6411 $svc->{$attr} = $value;
711 56         200 return 1;
712             }
713              
714             sub unset {
715 5     5 0 7 my $svc = shift;
716 5         9 my $attr = shift;
717 5         9 $svc->{$attr} = undef;
718 5         14 return 1;
719             }
720              
721             =head2 tags
722              
723             Returns a hashref of tags
724              
725             =head2 tags_as_text
726              
727             Returns tag as a descriptive text.
728              
729             =head2 tag_list
730              
731             Returns a reference to a list of tags
732              
733             =cut
734              
735             sub tags_as_text {
736 0     0 1 0 my $svc = shift;
737 0         0 return join ", ", @{ $svc->tag_list };
  0         0  
738              
739             }
740              
741             sub tag_list {
742 3     3 1 9 my $svc = shift;
743 3         5 return [ keys %{ $svc->tags } ];
  3         75  
744             }
745              
746             sub set_cmd {
747 31 100   31 0 10531 my $value = (ref $_[1] ? _ARRAY($_[1]) : _STRING($_[1])) or return;
    100          
748 28         215 shift->_set('cmd', $value);
749             }
750              
751             sub set_cmd_from_con {
752 14     14 0 17 my $svc = shift;
753 14         24 my $value = shift;
754 14 100       36 return $svc->unset('cmd') unless defined $value;
755 9 100       35 if ($value =~ /^\[/) {
756 4     4   214 $value = try { decode_json($value) }
757             catch {
758 3     3   32 my $error = $_;
759 3         96 $svc->{ctrl}->log->error("Invalid JSON: $error");
760 3         660 return;
761 4         35 };
762             }
763 9         119 return $svc->set_cmd($value);
764             }
765              
766             sub set_desc {
767 0 0   0 0 0 my $value = _STRING($_[1]) or return;
768 0         0 $value =~ s/[\n\r\t\0]+//g; ## desc should be one line
769 0         0 shift->_set('desc', $value);
770             }
771              
772             sub set_tags {
773 3 50   3 0 1669 my $value = _STRING($_[1]) or return;
774 3         7 $value =~ s/\s+//g; ## no space in tags thanks
775 3         11 my %hash_value = map { $_ => 1 } split (',', $value);
  7         19  
776 3         11 shift->_set('tags', \%hash_value);
777             }
778              
779             sub set_add_env {
780 0     0 0 0 my $svc = shift;
781 0 0       0 my $value = _STRING($_[0]) or return;
782 0         0 my ($key, $val) = split /=/, $value, 2;
783 0         0 $svc->{ctrl}->log->debug( "Setting ENV{$key} to '$val'" );
784 0         0 $svc->add_env($key, $val);
785             }
786              
787             =head2 add_env($key => $value)
788              
789             Adds an environment key, value pair to the service
790              
791             =cut
792              
793             sub add_env {
794 2     2 1 1119 my $svc = shift;
795 2         7 my ($key, $value) = @_;
796 2         74 $svc->env->{$key} = $value;
797 2         24 return 1;
798             }
799              
800             =head2 clear_env()
801              
802             Resets service environment to empty.
803              
804             =cut
805              
806             sub clear_env {
807 1     1 1 4 my $svc = shift;
808 1         7 $svc->{env} = {};
809             }
810              
811             sub set_stopwait_secs {
812 5 50   5 0 70 my $value = _NUMBER($_[1]) or return;
813 5         31 shift->_set('stopwait_secs', $value);
814             }
815              
816             sub set_startwait_secs {
817 8 50   8 0 109 my $value = _NUMBER($_[1]) or return;
818 8         34 shift->_set('startwait_secs', $value);
819             }
820              
821             sub set_tie_stdin_to {
822 2 50   2 0 24 my $value = _STRING($_[1]) or return;
823 2         8 shift->_set('tie_stdin_to', $value);
824             }
825              
826             sub set_ignore_stderr {
827 0     0 0 0 my $value = _STRING($_[1]);
828 0 0       0 return unless defined $value;
829 0         0 shift->_set('ignore_stderr', $value);
830             }
831              
832             sub set_ignore_stdout {
833 0     0 0 0 my $value = _STRING($_[1]);
834 0 0       0 return unless defined $value;
835 0         0 shift->_set('ignore_stdout', $value);
836             }
837              
838             sub set_respawn_on_fail {
839 4     4 0 33 my $value = _STRING($_[1]);
840 4 50       17 return unless defined $value;
841 4         51 shift->_set('respawn_on_fail', $value);
842             }
843              
844             sub set_respawn_on_stop {
845 4     4 0 36 my $value = _STRING($_[1]);
846 4 50       18 return unless defined $value;
847 4         45 shift->_set('respawn_on_stop', $value);
848             }
849              
850             sub set_respawn_max_retries {
851 2     2 0 1348 my $value = _POSINT($_[1]);
852 2 50       80 return unless defined $value;
853 2         96 shift->_set('respawn_max_retries', $value);
854             }
855              
856             sub set_no_new_session {
857 0     0 0 0 my $value = _STRING($_[1]);
858 0 0       0 return unless defined $value;
859 0         0 shift->_set('no_new_session', $value);
860             }
861              
862             sub _run_cmd {
863 29     29   63 my $svc = shift;
864 29         70 my $ctrl = $svc->{ctrl};
865 29         965 my $svcname = $svc->name;
866 29         852 $ctrl->log->info( sprintf "starting %s", $svcname );
867              
868 29         3552 my %stds = (
869             "<" => "/dev/null",
870             ">" => "/dev/null",
871             "2>" => "/dev/null",
872             );
873 29 100       960 if (my $sockname = $svc->tie_stdin_to) {
874 3         139 my $socket = $ctrl->socket($sockname);
875 3 50       28 if ($socket) {
876 3 50       146 if ($socket->is_bound) {
877 3         90 $ctrl->log->debug(
878             "Socket '$sockname' piped to stdin for '$svcname'"
879             );
880 3         717 $stds{"<"} = $socket->fh;
881             }
882             else {
883             ## That's a bit annoying should we try to connect?
884             ## XXX probably
885 0         0 $ctrl->log->error(
886             "Socket '$sockname' not bound. Fatal '$svcname'"
887             )
888             }
889             }
890             }
891              
892             ## what happens when the config changes?
893             ## watcher *won't* get redefined leading to configuration
894             ## not being takin into account until restart of the svc.
895             ## should we have a watcher reloading function? that will
896 29 50       950 if (my $logger = $ctrl->log) {
897             ## XXX verify leaks
898 29 50       962 unless ($svc->ignore_stdout) {
899 29         504 $stds{">"} = $logger->svc_watcher(out => $svc);
900             }
901 29 50       1343 unless ($svc->ignore_stderr ) {
902 29         389 $stds{"2>"} = $logger->svc_watcher(err => $svc);
903             }
904             }
905             $svc->{child_cv} = AnyEvent::Util::run_cmd(
906             $svc->cmd,
907             close_all => 1,
908             on_prepare => sub {
909 0     0   0 $svc->prepare_child;
910             },
911 29         853 '$$' => \$svc->{pid},
912             %stds,
913             );
914             $svc->{child_cv}->cb( sub {
915 28     28   2755 my $es = shift()->recv;
916 28         682 $svc->acknowledge_exit($es);
917 29         119621 });
918 29         3294 return 1;
919             }
920              
921             sub prepare_child {
922 0     0 0 0 my $svc = shift;
923 0 0       0 unless ($svc->no_new_session) {
924 0         0 my $sessid = POSIX::setsid();
925 0 0       0 $svc->{ctrl}->log->error("cannot create new session for service")
926             unless $sessid;
927             }
928 0         0 $svc->setup_environment;
929 0         0 return;
930             }
931              
932             =head2 setup_environment
933              
934             Executed in the child before exec, to take service's configured C and
935             populate C<%ENV> with it.
936              
937             =cut
938              
939             sub setup_environment {
940 0     0 1 0 my $svc = shift;
941 0         0 my $env = $svc->env;
942 0 0       0 return unless $env;
943 0 0       0 return unless ref $env eq 'HASH';
944 0         0 while (my ($k, $v) = each %$env) {
945 0         0 $ENV{$k} = $v;
946             }
947 0         0 $ENV{CONTROL_FREAK_ENABLED} = 1;
948 0         0 $ENV{CONTROL_FREAK_SERVICE} = $svc->name;
949 0         0 return 1;
950             }
951              
952             sub acknowledge_exit {
953 32     32 0 101 my $svc = shift;
954 32         87 my $es = shift;
955              
956 32         213 my $ctrl = $svc->{ctrl};
957 32         2745 my $name = $svc->name;
958 32         263 my $on_stop = $svc->{on_stop_cb};
959              
960             ## reset timers, set basic new state
961 32         197 $svc->{on_stop_cb} = undef;
962 32         93 $svc->{stop_cv} = undef;
963 32         280 $svc->{start_cv} = undef;
964 32         470 $svc->{child_cv} = undef;
965 32         83 $svc->{pid} = undef;
966 32         263 $svc->{exit_status} = $es;
967              
968 32 100 100     956 if (POSIX::WIFEXITED($es) && !POSIX::WEXITSTATUS($es)) {
    100 100        
969 5         2450 $ctrl->log->info("child $name exited");
970 5         838 $svc->{normal_exit} = 1;
971             }
972             elsif (POSIX::WIFSIGNALED($es) && POSIX::WTERMSIG($es) == SIGTERM) {
973 12         354 $ctrl->log->info("child $name gracefully killed");
974             }
975             else {
976 15         312 return $svc->deal_with_failure;
977             }
978 17         1516 $svc->{state} = 'stopped';
979 17 100       72 $on_stop->() if $on_stop;
980 17         890 $svc->optionally_respawn;
981             }
982              
983             ## What to do when process doesn't exit cleanly
984             sub deal_with_failure {
985 15     15 0 59 my $svc = shift;
986              
987 15         57 my $es = $svc->{exit_status};
988 15         291 my $r = ControlFreak::Util::exit_reason( $es );
989 15         576 $svc->{ctrl}->log->error("child terminated abnormally $es: $r");
990              
991             ## If we don't respawn on fail... just fail
992 15 100       2594 if (! $svc->respawn_on_fail) {
993 4         57 $svc->{state} = 'fail';
994 4         25 return;
995             }
996              
997             ## If we wanted the service down. Keep it that way.
998 11 100       128 if ($svc->{wants_down}) {
999 1         9 $svc->{state} = 'fail';
1000 1         4 return;
1001             }
1002              
1003             ## If the service failed while starting, enter backoff loop
1004 10 100       62 if ($svc->is_starting) {
1005 9   50     70 my $n = ++$svc->{backoff_retry} || 1;
1006 9 100       63 if ($n >= $svc->{respawn_max_retries}) {
1007             ## Exhausted options: bail
1008 2         26 $svc->{state} = 'fatal';
1009 2         10 $svc->{backoff_cv} = undef;
1010 2         9 return;
1011             }
1012 7         39 $svc->{state} = "backoff";
1013 7         84 my $backoff_delay = $svc->_exponential_backoff_delay($n);
1014 7         26 $svc->{backoff_retry} = $n;
1015             $svc->{backoff_cv} = AE::timer $backoff_delay, 0,
1016 7     4   113 sub { $svc->_backoff_restart };
  4         348650  
1017             }
1018             ## Otherwise, just restart the failed service
1019             else {
1020 1         15 $svc->{state} = 'fail';
1021 1         12 $svc->start;
1022             }
1023              
1024 8         91 return;
1025             }
1026              
1027             sub optionally_respawn {
1028 17     17 0 40 my $svc = shift;
1029 17 50       141 return unless $svc->is_stopped;
1030 17 100       684 return unless $svc->respawn_on_stop;
1031 4 100 66     82 return if !$svc->{normal_exit} # abnormal exits are not our business
1032             or $svc->{wants_down}; # we really want it down
1033 1         23 $svc->start;
1034 1         75 return;
1035             }
1036              
1037             =head1 AUTHOR
1038              
1039             Yann Kerherve Eyannk@cpan.orgE
1040              
1041             =head1 LICENSE
1042              
1043             This library is free software; you can redistribute it and/or modify
1044             it under the same terms as Perl itself.
1045              
1046             =head1 SEE ALSO
1047              
1048             L
1049              
1050             =cut
1051              
1052             1;