File Coverage

lib/Ubic/Daemon.pm
Criterion Covered Total %
statement 186 306 60.7
branch 77 168 45.8
condition 10 21 47.6
subroutine 22 28 78.5
pod 3 3 100.0
total 298 526 56.6


line stmt bran cond sub pod time code
1             package Ubic::Daemon;
2             $Ubic::Daemon::VERSION = '1.59';
3 34     34   555301 use strict;
  34         39  
  34         826  
4 34     34   102 use warnings;
  34         40  
  34         654  
5              
6             # ABSTRACT: daemon management utilities
7              
8              
9 34     34   555 use IO::Handle;
  34         6126  
  34         982  
10 34     34   13253 use IO::Select;
  34         39018  
  34         1429  
11 34     34   6190 use POSIX qw(setsid :sys_wait_h);
  34         59839  
  34         219  
12 34     34   21686 use Time::HiRes qw(sleep);
  34         13073  
  34         163  
13 34     34   3316 use Params::Validate qw(:all);
  34         40  
  34         4137  
14 34     34   144 use Carp;
  34         43  
  34         1626  
15 34     34   137 use Config;
  34         46  
  34         1120  
16              
17 34     34   3718 use Ubic::Lockf;
  34         47  
  34         1364  
18 34     34   3234 use Ubic::AccessGuard;
  34         49  
  34         772  
19 34     34   9295 use Ubic::Daemon::Status;
  34         48  
  34         875  
20 34     34   8812 use Ubic::Daemon::PidState;
  34         61  
  34         995  
21              
22 34     34   194 use parent qw(Exporter);
  34         43  
  34         202  
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   235 my %module = (
31             linux => 'Linux',
32             );
33              
34             # UBIC_DAEMON_OS support is here only for tests
35 42   50     339 my $module = $ENV{UBIC_DAEMON_OS} || $module{$^O} || 'POSIX';
36              
37 42         9582 require "Ubic/Daemon/OS/$module.pm";
38 42         1693 $OS = eval "Ubic::Daemon::OS::$module->new";
39 42 50       451 unless ($OS) {
40 0         0 die "failed to initialize OS-specific module $module: $@";
41             }
42 42         5369 __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 54     54   133 my $fh = shift;
59 54 100       196 return unless defined $fh;
60 42         82 print {$fh} '[', scalar(localtime), "]\t$$\t", @_, "\n";
  42         3187  
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 90     90 1 37031749 my ($pidfile, @tail) = validate_pos(@_, { type => SCALAR }, 0);
91 87         2358 my $options = validate(@tail, {
92             timeout => { default => 30, regex => qr/^\d+$/ },
93             });
94 84 50       873 my $timeout = $options->{timeout} if defined $options->{timeout};
95              
96             # TODO - move this check into Ubic::Daemon::PidState
97 84         1521 my $pid_state = Ubic::Daemon::PidState->new($pidfile);
98 84 100       277 return 'not running' if $pid_state->is_empty;
99              
100 73         380 my $piddata = $pid_state->read;
101 73 50       396 unless ($piddata) {
102 0         0 return 'not running';
103             }
104 73         181 my $pid = $piddata->{pid};
105              
106 73 50       372 unless (check_daemon($pidfile)) {
107 0         0 return 'not running';
108             }
109 73         6003 kill 15 => $pid;
110 73         175 my $trial = 1;
111             {
112 73         119 my $sleep = 0.1;
  73         161  
113 73         107 my $total_sleep = 0;
114 73         77 while (1) {
115 221 100       1672 unless (check_daemon($pidfile)) {
116 70         1864 return 'stopped';
117             }
118 151 100       637 last if $total_sleep >= $timeout;
119 148         49020212 sleep($sleep);
120 148         948 $total_sleep += $sleep;
121 148 100       3068 $sleep += 0.1 * $trial if $sleep < 1;
122 148         464 $trial++;
123             }
124             }
125 3 50       12 unless (check_daemon($pidfile)) {
126 0         0 return 'stopped';
127             }
128 3         81 die "failed to stop daemon with pidfile '$pidfile' (pid $pid, timeout $timeout, trials $trial)";
129             }
130              
131             sub start_daemon($) {
132 174     174 1 31434280 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 173         3639 = @options{qw/ bin function name pidfile stdout stderr ubic_log term_timeout cwd env credentials start_hook proxy_logs /};
149 173 50 66     808 if (not defined $bin and not defined $function) {
150 0         0 croak "One of 'bin' and 'function' should be specified";
151             }
152 173 50 66     1184 if (defined $bin and defined $function) {
153 0         0 croak "Only one of 'bin' and 'function' should be specified";
154             }
155 173 100       527 unless (defined $name) {
156 135 100       389 if (ref $bin) {
157 81         379 $name = join ' ', @$bin;
158             }
159             else {
160 54   50     190 $name = $bin || 'anonymous';
161             }
162             }
163              
164 173 100       624 if (check_daemon($pidfile)) {
165 13         4836 croak "Daemon with pidfile $pidfile already running, can't start";
166             }
167              
168 160         578 my $pid_state = Ubic::Daemon::PidState->new($pidfile);
169 160         581 $pid_state->init;
170              
171 160 50       6276 pipe my ($read_pipe, $write_pipe) or die "pipe failed";
172 160         233 my $child;
173              
174 160 100       108257 unless ($child = fork) {
175 18 50       1179 die "fork failed" unless defined $child;
176              
177 18         205 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 18         1120 };
186              
187 18         651 eval {
188 18 50       728 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 18 50       164 my $tmp_pid = fork() and POSIX::_exit(0); # detach from parent process
  18         13482  
193 18 50       1370 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 18 100       363 if ($bin) {
200 16         248 my $write_pipe_fd_num = fileno($write_pipe);
201 16         976 $OS->close_all_fh($write_pipe_fd_num); # except pipe
202             }
203              
204             my $open_handles_sub = sub {
205 18     18   63 my $guard;
206 18 50       98 $guard = Ubic::AccessGuard->new($credentials) if $credentials;
207 18 50       2359 open STDOUT, ">>", $stdout or die "Can't write to '$stdout': $!";
208 18 50       538 open STDERR, ">>", $stderr or die "Can't write to '$stderr': $!";
209 18         1269 STDOUT->autoflush(1);
210 18         2974 STDERR->autoflush(1);
211 18 100       452 if (defined $ubic_log) {
212 14 50       1004 open $ubic_fh, ">>", $ubic_log or die "Can't write to '$ubic_log': $!";
213 14         60 $ubic_fh->autoflush(1);
214             }
215 18         607 };
216 18         128 $open_handles_sub->();
217 18         413 my $stdin = '/dev/null';
218 18 50       388 open STDIN, "<", $stdin or die "Can't read from '$stdin': $!";
219              
220 18         556 $SIG{HUP} = 'ignore';
221 18         506 $0 = "ubic-guardian $name";
222 18         501 setsid; # ubic-daemon gets it's own session
223 18         309 _log($ubic_fh, "guardian name: $0");
224              
225 18         93 _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 18 50       380 $lock = $pid_state->lock(5) or die "Can't lock $pid_state";
232              
233 18         148 $pid_state->remove;
234 18         82 _log($ubic_fh, "got lock");
235              
236 18         540 my %daemon_pipes;
237 18 100       138 if (defined $proxy_logs) {
238 2         14 for my $handle (qw/stdout stderr/) {
239 4 50       92 pipe my ($read, $write) or die "pipe for daemon $handle failed";
240 4         38 $daemon_pipes{$handle} = {read => $read, write => $write};
241             }
242             }
243 18         113 my $child;
244 18 50       15832 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 18 50       457 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 18         859 setpgrp;
355 18         889 $0 = "ubic-daemon $name";
356              
357 18 50       203 if (defined $cwd) {
358 0 0       0 chdir $cwd or die "chdir to '$cwd' failed: $!";
359             }
360 18 50       157 if (defined $env) {
361 0         0 for my $key (keys %{ $env }) {
  0         0  
362 0         0 $ENV{$key} = $env->{$key};
363             }
364             }
365 18 100       240 $start_hook->() if $start_hook;
366 18 50       322 $credentials->set() if $credentials;
367              
368 18 100       570 close($ubic_fh) if defined $ubic_fh;
369 18         630 $lock->dissolve;
370              
371 18 50       44 print {$write_pipe} "execing into daemon\n" or die "Can't write to pipe: $!";
  18         448  
372 18 50       711 close($write_pipe) or die "Can't close pipe: $!";
373 18         228 undef $write_pipe;
374              
375 18 100       86 if (defined $proxy_logs) {
376             # redirecting standard streams to pipes
377 2   50     65 close($daemon_pipes{$_}{read}) or die "Can't close $_ read: $!" for qw/stdout stderr/;
378 2 50       72 open STDOUT, '>&=', $daemon_pipes{stdout}{write} or die "Can't open stdout write: $!";
379 2 50       59 open STDERR, '>&=', $daemon_pipes{stderr}{write} or die "Can't open stderr write: $!";
380             }
381              
382             # finally, run underlying binary
383 18 100       197 if (ref $bin) {
    100          
384 12 0       0 exec(@$bin) or die "exec failed: $!";
385             }
386             elsif ($bin) {
387 4 0       0 exec($bin) or die "exec failed: $!";
388             }
389             else {
390 2         31 $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 142         215862 waitpid($child, 0); # child should've exited immediately
401 142 50       2057 close($write_pipe) or die "Can't close write_pipe: $!";
402              
403 142         1181 my $out = '';
404 142         1223132 while ( my $data = <$read_pipe>) {
405 284         29273027 $out .= $data;
406             }
407 142 50       3028 close($read_pipe) or die "Can't close read_pipe: $!";
408 142 100 66     6501 if ($out =~ /^execing into daemon$/m and $out =~ /^pidfile written$/m) {
409             # TODO - check daemon's name to make sure that exec happened
410 130         9663 return;
411             }
412 12         624 die "Failed to create daemon: '$out'";
413             }
414              
415             sub check_daemon {
416 679     679 1 13004624 my $pidfile = shift;
417 679         12897 my $options = validate(@_, {
418             quiet => { optional => 1 },
419             });
420              
421             my $print = sub {
422 10 50   10   30 print @_, "\n" unless $options->{quiet};
423 679         5151 };
424              
425 679         7904 my $pid_state = Ubic::Daemon::PidState->new($pidfile);
426 679 100       2126 return undef if $pid_state->is_empty;
427              
428 350         1646 my $lock = $pid_state->lock;
429 350         1695 my $piddata = $pid_state->read;
430 350 100       1076 unless ($lock) {
431             # locked => daemon is alive
432 345         4175 return Ubic::Daemon::Status->new({ pid => $piddata->{daemon}, guardian_pid => $piddata->{pid} });
433             }
434              
435 5 50       25 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     135 if ($piddata->{format} and $piddata->{format} eq 'old') {
442 0         0 die "deprecated pidfile format detected\n";
443             }
444 5 50       35 unless ($piddata->{daemon}) {
445 34     34   91748 use Data::Dumper;
  34         168405  
  34         9265  
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       55 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         45 my $daemon_cmd = $OS->pid2cmd($piddata->{daemon});
456              
457 5         40 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       15 if ($guid eq $piddata->{guid}) {
463 5         85 $print->("killing unguarded daemon '$daemon_cmd' with pid $piddata->{daemon} from $pidfile");
464 5         85 kill -9 => $piddata->{daemon};
465 5         30 $pid_state->remove;
466 5         30 $print->("pidfile $pidfile removed");
467 5         55 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__