File Coverage

lib/Ubic/Daemon.pm
Criterion Covered Total %
statement 190 306 62.0
branch 80 168 47.6
condition 10 21 47.6
subroutine 22 28 78.5
pod 3 3 100.0
total 305 526 57.9


line stmt bran cond sub pod time code
1             package Ubic::Daemon;
2             $Ubic::Daemon::VERSION = '1.58_01'; # TRIAL
3 36     36   954729 use strict;
  36         68  
  36         1207  
4 36     36   196 use warnings;
  36         66  
  36         1206  
5              
6             # ABSTRACT: daemon management utilities
7              
8              
9 36     36   858 use IO::Handle;
  36         6792  
  36         1552  
10 36     36   21518 use IO::Select;
  36         58374  
  36         2388  
11 36     36   9005 use POSIX qw(setsid :sys_wait_h);
  36         100901  
  36         353  
12 36     36   38151 use Time::HiRes qw(sleep);
  36         19161  
  36         298  
13 36     36   4936 use Params::Validate qw(:all);
  36         76  
  36         6177  
14 36     36   226 use Carp;
  36         54  
  36         2382  
15 36     36   195 use Config;
  36         67  
  36         1700  
16              
17 36     36   5458 use Ubic::Lockf;
  36         63  
  36         2077  
18 36     36   6610 use Ubic::AccessGuard;
  36         73  
  36         1218  
19 36     36   15041 use Ubic::Daemon::Status;
  36         87  
  36         1340  
20 36     36   13379 use Ubic::Daemon::PidState;
  36         77  
  36         1329  
21              
22 36     36   244 use parent qw(Exporter);
  36         56  
  36         262  
23             our @EXPORT_OK = qw(start_daemon stop_daemon check_daemon);
24             our %EXPORT_TAGS = (
25             all => \@EXPORT_OK,
26             );
27              
28             our $OS;
29             sub import {
30 42     42   284 my %module = (
31             linux => 'Linux',
32             );
33              
34             # UBIC_DAEMON_OS support is here only for tests
35 42   50     507 my $module = $ENV{UBIC_DAEMON_OS} || $module{$^O} || 'POSIX';
36              
37 42         14728 require "Ubic/Daemon/OS/$module.pm";
38 42         2075 $OS = eval "Ubic::Daemon::OS::$module->new";
39 42 50       604 unless ($OS) {
40 0         0 die "failed to initialize OS-specific module $module: $@";
41             }
42 42         7453 __PACKAGE__->export_to_level(1, @_);
43             }
44              
45             {
46             my @signame;
47             sub _signame {
48 0     0   0 my $signum = shift;
49 0 0       0 unless (@signame) {
50 0         0 @signame = split /\s+/, $Config{sig_name};
51             }
52 0         0 return $signame[$signum];
53              
54             }
55             }
56              
57             sub _log {
58 63     63   486 my $fh = shift;
59 63 100       2367 return unless defined $fh;
60 42         99 print {$fh} '[', scalar(localtime), "]\t$$\t", @_, "\n";
  42         4340  
61             }
62              
63             sub _log_exit_code {
64 0     0   0 my ($fh, $code, $pid) = @_;
65 0 0       0 if ($code == 0) {
66 0         0 _log($fh, "daemon $pid exited");
67 0         0 return;
68             }
69              
70 0         0 my $msg = "daemon $pid failed with \$? = $?";
71 0 0       0 if (my $signal = $? & 127) {
    0          
    0          
72 0         0 my $signame = _signame($signal);
73 0 0       0 if (defined $signame) {
74 0         0 $msg = "daemon $pid failed with signal $signame ($signal)";
75             }
76             else {
77 0         0 $msg = "daemon $pid failed with signal $signal";
78             }
79             }
80             elsif ($? & 128) {
81 0         0 $msg = "daemon $pid failed, core dumped";
82             }
83             elsif (my $code = $? >> 8) {
84 0         0 $msg = "daemon $pid failed, exit code $code";
85             }
86 0         0 _log($fh, $msg);
87             }
88              
89             sub stop_daemon($;@) {
90 96     96 1 43414876 my ($pidfile, @tail) = validate_pos(@_, { type => SCALAR }, 0);
91 93         2849 my $options = validate(@tail, {
92             timeout => { default => 30, regex => qr/^\d+$/ },
93             });
94 90 50       1226 my $timeout = $options->{timeout} if defined $options->{timeout};
95              
96             # TODO - move this check into Ubic::Daemon::PidState
97 90         1657 my $pid_state = Ubic::Daemon::PidState->new($pidfile);
98 90 100       838 return 'not running' if $pid_state->is_empty;
99              
100 79         538 my $piddata = $pid_state->read;
101 79 50       361 unless ($piddata) {
102 0         0 return 'not running';
103             }
104 79         330 my $pid = $piddata->{pid};
105              
106 79 50       477 unless (check_daemon($pidfile)) {
107 0         0 return 'not running';
108             }
109 79         6828 kill 15 => $pid;
110 79         304 my $trial = 1;
111             {
112 79         141 my $sleep = 0.1;
  79         137  
113 79         830 my $total_sleep = 0;
114 79         118 while (1) {
115 241 100       1505 unless (check_daemon($pidfile)) {
116 76         1505 return 'stopped';
117             }
118 165 100       784 last if $total_sleep >= $timeout;
119 162         55281051 sleep($sleep);
120 162         942 $total_sleep += $sleep;
121 162 100       1325 $sleep += 0.1 * $trial if $sleep < 1;
122 162         559 $trial++;
123             }
124             }
125 3 50       27 unless (check_daemon($pidfile)) {
126 0         0 return 'stopped';
127             }
128 3         96 die "failed to stop daemon with pidfile '$pidfile' (pid $pid, timeout $timeout, trials $trial)";
129             }
130              
131             sub start_daemon($) {
132 183     183 1 31663390 my %options = validate(@_, {
133             bin => { type => SCALAR | ARRAYREF, optional => 1 },
134             function => { type => CODEREF, optional => 1 },
135             name => { type => SCALAR, optional => 1 },
136             pidfile => { type => SCALAR },
137             stdout => { type => SCALAR, default => '/dev/null' },
138             stderr => { type => SCALAR, default => '/dev/null' },
139             ubic_log => { type => SCALAR, optional => 1 },
140             term_timeout => { type => SCALAR, default => 10, regex => qr/^\d+$/ },
141             cwd => { type => SCALAR, optional => 1 },
142             env => { type => HASHREF, optional => 1 },
143             proxy_logs => { type => BOOLEAN, optional => 1 },
144             credentials => { isa => 'Ubic::Credentials', optional => 1 },
145             start_hook => { type => CODEREF, optional => 1 },
146             });
147             my ($bin, $function, $name, $pidfile, $stdout, $stderr, $ubic_log, $term_timeout, $cwd, $env, $credentials, $start_hook, $proxy_logs)
148 182         4501 = @options{qw/ bin function name pidfile stdout stderr ubic_log term_timeout cwd env credentials start_hook proxy_logs /};
149 182 50 66     1238 if (not defined $bin and not defined $function) {
150 0         0 croak "One of 'bin' and 'function' should be specified";
151             }
152 182 50 66     1496 if (defined $bin and defined $function) {
153 0         0 croak "Only one of 'bin' and 'function' should be specified";
154             }
155 182 100       628 unless (defined $name) {
156 144 100       512 if (ref $bin) {
157 89         429 $name = join ' ', @$bin;
158             }
159             else {
160 55   50     223 $name = $bin || 'anonymous';
161             }
162             }
163              
164 182 100       1820 if (check_daemon($pidfile)) {
165 13         4420 croak "Daemon with pidfile $pidfile already running, can't start";
166             }
167              
168 169         668 my $pid_state = Ubic::Daemon::PidState->new($pidfile);
169 169         717 $pid_state->init;
170              
171 169 50       6267 pipe my ($read_pipe, $write_pipe) or die "pipe failed";
172 169         297 my $child;
173              
174 169 100       186886 unless ($child = fork) {
175 21 50       1764 die "fork failed" unless defined $child;
176              
177 21         265 my $ubic_fh;
178             my $lock;
179             my $instant_exit = sub {
180 0 0   0   0 close($ubic_fh) if $ubic_fh;
181 0         0 STDOUT->flush;
182 0         0 STDERR->flush;
183 0         0 undef $lock;
184 0         0 POSIX::_exit(0); # don't allow any cleanup to happen - this process was forked from unknown environment, don't want to run unknown destructors
185 21         1648 };
186              
187 21         924 eval {
188 21 50       1182 close($read_pipe) or die "Can't close read pipe: $!";
189             # forking child - will reopen standard streams, daemonize itself, fork into daemon binary and wait for it
190              
191             {
192 21 50       168 my $tmp_pid = fork() and POSIX::_exit(0); # detach from parent process
  21         24393  
193 21 50       2053 die "fork failed" unless defined $tmp_pid;
194             }
195              
196             # Close all inherited filehandles except $write_pipe (it will be closed explicitly).
197             # Do not close fh if uses 'function' option instead of 'bin'
198             # ('function' support should be removed altogether because of this, actually; it's evil).
199 21 100       538 if ($bin) {
200 19         346 my $write_pipe_fd_num = fileno($write_pipe);
201 19         1732 $OS->close_all_fh($write_pipe_fd_num); # except pipe
202             }
203              
204             my $open_handles_sub = sub {
205 21     21   128 my $guard;
206 21 50       212 $guard = Ubic::AccessGuard->new($credentials) if $credentials;
207 21 50       3455 open STDOUT, ">>", $stdout or die "Can't write to '$stdout': $!";
208 21 50       1121 open STDERR, ">>", $stderr or die "Can't write to '$stderr': $!";
209 21         2122 STDOUT->autoflush(1);
210 21         4456 STDERR->autoflush(1);
211 21 100       734 if (defined $ubic_log) {
212 14 50       1317 open $ubic_fh, ">>", $ubic_log or die "Can't write to '$ubic_log': $!";
213 14         131 $ubic_fh->autoflush(1);
214             }
215 21         1015 };
216 21         286 $open_handles_sub->();
217 21         573 my $stdin = '/dev/null';
218 21 50       779 open STDIN, "<", $stdin or die "Can't read from '$stdin': $!";
219              
220 21         828 $SIG{HUP} = 'ignore';
221 21         876 $0 = "ubic-guardian $name";
222 21         866 setsid; # ubic-daemon gets it's own session
223 21         550 _log($ubic_fh, "guardian name: $0");
224              
225 21         149 _log($ubic_fh, "obtaining lock...");
226              
227             # We're passing 'timeout' option to lockf call to get rid of races.
228             # There should be no races when Ubic::Daemon is used in context of
229             # ubic service, because services have an additional lock, but
230             # Ubic::Daemon can be useful without services as well.
231 21 50       669 $lock = $pid_state->lock(5) or die "Can't lock $pid_state";
232              
233 21         195 $pid_state->remove;
234 21         154 _log($ubic_fh, "got lock");
235              
236 21         1035 my %daemon_pipes;
237 21 100       140 if (defined $proxy_logs) {
238 2         23 for my $handle (qw/stdout stderr/) {
239 4 50       207 pipe my ($read, $write) or die "pipe for daemon $handle failed";
240 4         55 $daemon_pipes{$handle} = {read => $read, write => $write};
241             }
242             }
243 21         55 my $child;
244 21 50       32683 if ($child = fork) {
245             # guardian
246              
247 0         0 _log($ubic_fh, "guardian pid: $$");
248 0         0 _log($ubic_fh, "daemon pid: $child");
249              
250 0         0 my $child_guid = $OS->pid2guid($child);
251 0 0       0 unless ($child_guid) {
252 0 0       0 if ($OS->pid_exists($child)) {
253 0         0 die "Can't detect guid";
254             }
255 0         0 $? = 0;
256 0 0       0 unless (waitpid($child, WNOHANG) == $child) {
257 0         0 die "No pid $child but waitpid didn't collect $child status";
258             }
259 0         0 _log_exit_code($ubic_fh, $?, $child);
260 0         0 $pid_state->remove();
261 0         0 die "daemon exited immediately";
262             }
263 0         0 _log($ubic_fh, "child guid: $child_guid");
264 0         0 $pid_state->write({ pid => $child, guid => $child_guid });
265              
266             my $kill_sub = sub {
267 0 0   0   0 if ($term_timeout) {
268 0         0 _log($ubic_fh, "SIGTERM timeouted after $term_timeout second(s)");
269             }
270 0         0 _log($ubic_fh, "sending SIGKILL to $child");
271 0         0 kill -9 => $child;
272 0         0 _log($ubic_fh, "daemon $child probably killed by SIGKILL");
273 0         0 $pid_state->remove();
274 0         0 $instant_exit->();
275 0         0 };
276              
277 0         0 my $sigterm_sent;
278             $SIG{TERM} = sub {
279 0 0   0   0 if ($term_timeout > 0) {
280 0         0 $SIG{ALRM} = $kill_sub;
281 0         0 alarm($term_timeout);
282 0         0 _log($ubic_fh, "sending SIGTERM to $child");
283 0         0 kill -15 => $child;
284 0         0 $sigterm_sent = 1;
285             }
286             else {
287 0         0 $kill_sub->();
288             }
289 0         0 };
290 0 0       0 print {$write_pipe} "pidfile written\n" or die "Can't write to pipe: $!";
  0         0  
291 0 0       0 close $write_pipe or die "Can't close pipe: $!";
292 0         0 undef $write_pipe;
293              
294 0 0       0 if (defined $proxy_logs) {
295             $SIG{HUP} = sub {
296 0     0   0 eval { $open_handles_sub->() };
  0         0  
297 0 0       0 if ($@) {
298 0         0 _log($ubic_fh, "failed to reopen stdout/stderr handles: $@");
299 0         0 $kill_sub->();
300             }
301             else {
302 0         0 _log($ubic_fh, "reopened stdout/stderr");
303             }
304 0         0 };
305              
306 0         0 for my $handle (qw/stdout stderr/) {
307 0 0       0 close($daemon_pipes{$handle}{write}) or do {
308 0         0 _log($ubic_fh, "Can't close $handle write: $!");
309 0         0 die "Can't close $handle write: $!"
310             };
311             }
312 0         0 my $sel = IO::Select->new();
313 0         0 $sel->add($daemon_pipes{stdout}{read}, $daemon_pipes{stderr}{read});
314 0         0 my $BUFF_SIZE = 4096;
315             READ:
316 0         0 while ($OS->pid_exists($child)) { # this loop is needed because of timeout in can_read
317 0         0 while (my @ready = $sel->can_read(1)) {
318 0         0 my $exhausted = 0;
319 0         0 for my $handle (@ready) {
320 0         0 my $data;
321 0         0 my $bytes_read = sysread($handle, $data, $BUFF_SIZE);
322 0 0       0 die "Can't poll $handle: $!" unless defined $bytes_read; # handle EWOULDBLOCK?
323 0 0       0 $exhausted += 1 if $bytes_read == 0;
324 0 0       0 if (fileno $handle == fileno $daemon_pipes{stdout}{read}) {
325 0         0 print STDOUT $data;
326             }
327 0 0       0 if (fileno $handle == fileno $daemon_pipes{stderr}{read}) {
328 0         0 print STDERR $data;
329             }
330             }
331 0 0       0 last READ if $exhausted == @ready;
332             }
333             }
334             }
335              
336 0         0 $? = 0;
337 0         0 waitpid($child, 0);
338 0         0 my $code = $?;
339 0 0 0     0 if ($sigterm_sent and ($code & 127) == &POSIX::SIGTERM) {
340             # it's ok, we probably sent this signal ourselves
341 0         0 _log($ubic_fh, "daemon $child exited by sigterm");
342             }
343             else {
344 0         0 _log_exit_code($ubic_fh, $code, $child);
345             }
346 0         0 $pid_state->remove;
347             }
348             else {
349             # daemon
350              
351 21 50       662 die "fork failed" unless defined $child;
352              
353             # start new process group - become immune to kills at parent group and at the same time be able to kill all processes below
354 21         1102 setpgrp;
355 21         1253 $0 = "ubic-daemon $name";
356              
357 21 100       358 if (defined $cwd) {
358 1 50       29 chdir $cwd or die "chdir to '$cwd' failed: $!";
359             }
360 21 100       254 if (defined $env) {
361 1         4 for my $key (keys %{ $env }) {
  1         36  
362 2         72 $ENV{$key} = $env->{$key};
363             }
364             }
365 21 100       282 $start_hook->() if $start_hook;
366 21 50       465 $credentials->set() if $credentials;
367              
368 21 100       792 close($ubic_fh) if defined $ubic_fh;
369 21         1124 $lock->dissolve;
370              
371 21 50       51 print {$write_pipe} "execing into daemon\n" or die "Can't write to pipe: $!";
  21         600  
372 21 50       1491 close($write_pipe) or die "Can't close pipe: $!";
373 21         462 undef $write_pipe;
374              
375 21 100       176 if (defined $proxy_logs) {
376             # redirecting standard streams to pipes
377 2   50     74 close($daemon_pipes{$_}{read}) or die "Can't close $_ read: $!" for qw/stdout stderr/;
378 2 50       76 open STDOUT, '>&=', $daemon_pipes{stdout}{write} or die "Can't open stdout write: $!";
379 2 50       28 open STDERR, '>&=', $daemon_pipes{stderr}{write} or die "Can't open stderr write: $!";
380             }
381              
382             # finally, run underlying binary
383 21 100       275 if (ref $bin) {
    100          
384 14 0       0 exec(@$bin) or die "exec failed: $!";
385             }
386             elsif ($bin) {
387 5 0       0 exec($bin) or die "exec failed: $!";
388             }
389             else {
390 2         27 $function->();
391             }
392             }
393             };
394 0 0       0 if ($write_pipe) {
395 0         0 print {$write_pipe} "Error: $@\n";
  0         0  
396 0         0 $write_pipe->flush;
397             }
398 0         0 $instant_exit->();
399             }
400 148         351273 waitpid($child, 0); # child should've exited immediately
401 148 50       3770 close($write_pipe) or die "Can't close write_pipe: $!";
402              
403 148         1732 my $out = '';
404 148         554080 while ( my $data = <$read_pipe>) {
405 296         365449704 $out .= $data;
406             }
407 148 50       3418 close($read_pipe) or die "Can't close read_pipe: $!";
408 148 100 66     5656 if ($out =~ /^execing into daemon$/m and $out =~ /^pidfile written$/m) {
409             # TODO - check daemon's name to make sure that exec happened
410 136         9597 return;
411             }
412 12         696 die "Failed to create daemon: '$out'";
413             }
414              
415             sub check_daemon {
416 756     756 1 13015272 my $pidfile = shift;
417 756         13833 my $options = validate(@_, {
418             quiet => { optional => 1 },
419             });
420              
421             my $print = sub {
422 10 50   10   40 print @_, "\n" unless $options->{quiet};
423 756         5980 };
424              
425 756         9981 my $pid_state = Ubic::Daemon::PidState->new($pidfile);
426 756 100       3877 return undef if $pid_state->is_empty;
427              
428 389         1982 my $lock = $pid_state->lock;
429 389         1893 my $piddata = $pid_state->read;
430 389 100       1322 unless ($lock) {
431             # locked => daemon is alive
432 384         9157 return Ubic::Daemon::Status->new({ pid => $piddata->{daemon}, guardian_pid => $piddata->{pid} });
433             }
434              
435 5 50       30 unless ($piddata) {
436 0         0 return undef;
437             }
438              
439             # acquired lock when pidfile exists
440             # checking whether just ubic-guardian died or whole process group
441 5 50 33     125 if ($piddata->{format} and $piddata->{format} eq 'old') {
442 0         0 die "deprecated pidfile format detected\n";
443             }
444 5 50       25 unless ($piddata->{daemon}) {
445 36     36   151916 use Data::Dumper;
  36         287878  
  36         14294  
446 0         0 die "pidfile $pidfile exists, but daemon pid is not saved in it, so existing unguarded daemon can't be killed (piddata: ".Dumper($piddata).")";
447             }
448 5 50       50 unless ($OS->pid_exists($piddata->{daemon})) {
449 0         0 $pid_state->remove;
450 0         0 $print->("pidfile $pidfile removed - daemon with cached pid $piddata->{daemon} not found");
451 0         0 return undef;
452             }
453              
454             # TODO - wrap in eval and return undef if pid2cmd fails?
455 5         65 my $daemon_cmd = $OS->pid2cmd($piddata->{daemon});
456              
457 5         30 my $guid = $OS->pid2guid($piddata->{daemon});
458 5 50       25 unless ($guid) {
459 0         0 $print->("daemon '$daemon_cmd' from $pidfile just disappeared");
460 0         0 return undef;
461             }
462 5 50       25 if ($guid eq $piddata->{guid}) {
463 5         30 $print->("killing unguarded daemon '$daemon_cmd' with pid $piddata->{daemon} from $pidfile");
464 5         80 kill -9 => $piddata->{daemon};
465 5         20 $pid_state->remove;
466 5         30 $print->("pidfile $pidfile removed");
467 5         75 return undef;
468             }
469 0           $print->("daemon pid $piddata->{daemon} cached in pidfile $pidfile, ubic-guardian not found");
470 0           $print->("current process '$daemon_cmd' with pid $piddata->{daemon} has wrong guid ($piddata->{guid}, expected $guid) and will not be killed");
471 0           $print->("removing pidfile $pidfile");
472 0           $pid_state->remove;
473 0           return undef;
474             }
475              
476              
477             1;
478              
479             __END__