File Coverage

blib/lib/App/Base/Daemon.pm
Criterion Covered Total %
statement 85 108 78.7
branch 23 56 41.0
condition 13 35 37.1
subroutine 18 20 90.0
pod 2 2 100.0
total 141 221 63.8


line stmt bran cond sub pod time code
1             package App::Base::Daemon;
2 9     9   320643 use 5.010;
  9         24  
3 9     9   2157 use Moose::Role;
  9         1792575  
  9         33  
4             with 'App::Base::Script::Common';
5              
6             =head1 NAME
7              
8             App::Base::Daemon - A lazy person's tool for writing self-documenting, self-monitoring daemons
9              
10             =head1 VERSION
11              
12             This document describes App::Base version 0.05
13              
14             =head1 SYNOPSIS
15              
16             package App::Base::Daemon::example;
17             use Moose;
18             with 'App::Base::Daemon';
19             sub documentation { return 'This is an example daemon.'; }
20              
21             sub options {
22              
23             # See App::Base::Script::Common
24             }
25              
26             sub daemon_run {
27             my $self = shift;
28             while (1) {
29             # do something
30             sleep(1)
31             }
32              
33             return 0; # This will never be reached
34             }
35              
36             sub handle_shutdown {
37             my $self = shift;
38             # do something
39             return 0;
40             }
41              
42             no Moose;
43             __PACKAGE__->meta->make_immutable;
44             1;
45              
46             exit App::Base::Daemon::example->new->run;
47              
48             =head1 DESCRIPTION
49              
50             App::Base::Daemon builds on App::Base::Script::Common and provides common infrastructure for writing daemons, including:
51              
52             =over 4
53              
54             =item -
55              
56             Standardized logging techniques via syslog
57              
58             =item -
59              
60             Signal processing and graceful shutdown
61              
62             =back
63              
64             =head1 BUILT-IN OPTIONS
65              
66             Every App::Base::Daemon-implementing class gets some daemon-specific options for
67             free, in addition to those provided by App::Base::Script::Common. They are:
68              
69             =head2 --no-fork
70              
71             Rather than double-forking and detaching from the console, the daemon
72             runs in the foreground (parent) process. Useful for debugging or
73             interactive invocations.
74              
75             =head2 --pid-file
76              
77             Writes PID of the daemon into specified file, by default writes pid into /var/run/__PACKAGE__.pid
78              
79             =head2 --no-pid-file
80              
81             Do not write pid file, and do not check if it is exist and locked.
82              
83             =head2 --no-warn
84              
85             Do not produce warnings, silent mode
86              
87             =head1 REQUIRED SUBCLASS METHODS
88              
89             =cut
90              
91 9     9   30501 use namespace::autoclean;
  9         18  
  9         48  
92 9     9   648 use Try::Tiny;
  9         9  
  9         462  
93 9     9   4827 use Path::Tiny;
  9         43491  
  9         474  
94              
95             =head2 daemon_run
96              
97             The main loop that runs the daemon. Typically this will include while(1) or
98             something similar. If this method returns, daemon exits.
99              
100             =cut
101              
102             requires 'daemon_run';
103              
104             =head2 handle_shutdown
105              
106             Called before the daemon shuts down in response to a shutdown signal. Should
107             clean up any resources in use by the daemon. The return value of
108             handle_shutdown is used as the exit status of the daemon.
109              
110             =cut
111              
112             requires 'handle_shutdown';
113              
114 9     9   2979 use Socket;
  9         15213  
  9         3138  
115 9     9   3090 use IO::Handle;
  9         25923  
  9         291  
116 9     9   3504 use File::Flock::Tiny;
  9         6897  
  9         207  
117 9     9   2406 use POSIX qw();
  9         30711  
  9         6942  
118              
119             =head1 ATTRIBUTES
120              
121             =head2 shutdown_signals
122              
123             An arrayref of signals that should result in termination of the daemon.
124             Defaults are: INT, QUIT, TERM.
125              
126             =cut
127              
128             has 'shutdown_signals' => (
129             is => 'ro',
130             default => sub {
131             [qw( INT QUIT TERM )];
132             },
133             );
134              
135             =head2 user
136              
137             Run as specified user, note that it is only possible if daemon started as root
138              
139             =cut
140              
141             has user => ( is => 'ro' );
142              
143             =head2 group
144              
145             Run as specified group, note that it is only possible if daemon started as root
146              
147             =cut
148              
149             has group => ( is => 'ro' );
150              
151             =head2 pid_file
152              
153             Pid file name
154              
155             =cut
156              
157             has pid_file => (
158             is => 'ro',
159             lazy => 1,
160             builder => '_build_pid_file',
161             );
162              
163             sub _build_pid_file {
164 19     19   37 my $self = shift;
165 19         51 my $file = $self->getOption('pid-file');
166 19 50       59 unless ($file) {
167 19         39 my $class = ref $self;
168 19   50     55 my $piddir = $ENV{APP_BASE_DAEMON_PIDDIR} || '/var/run';
169 19         50 $file = path($piddir)->child("$class.pid");
170             }
171 19         671 return "$file";
172             }
173              
174             =head2 can_do_hot_reload
175              
176             Should return true if implementation supports hot reloading
177              
178             =cut
179              
180 19     19 1 53 sub can_do_hot_reload { return }
181              
182             around 'base_options' => sub {
183             my $orig = shift;
184             my $self = shift;
185             return [
186             @{ $self->$orig },
187             {
188             name => 'no-fork',
189             documentation => "Do not detach and run in the background",
190             },
191             {
192             name => 'pid-file',
193             option_type => 'string',
194             documentation => "Use specified file to save PID",
195             },
196             {
197             name => 'no-pid-file',
198             documentation => "Do not check if pidfile exists and locked",
199             },
200             {
201             name => 'user',
202             documentation => "User to run as",
203             },
204             {
205             name => 'group',
206             documentation => "Group to run as",
207             },
208             { name => 'no-warn',
209             documentation => 'Do not produce warnings',
210             },
211             ];
212             };
213              
214             =head1 METHODS
215              
216             =cut
217              
218             sub _signal_shutdown {
219 3     3   16 my $self = shift;
220 3         13 my $sig = shift;
221 3         41 $self->handle_shutdown;
222 3         123 exit 0;
223             }
224              
225             sub __run {
226 23     23   29 my $self = shift;
227              
228 23         25 my $pid;
229 23   66     201 my $hot_reload = $ENV{APP_BASE_DAEMON_GEN}++ && $self->can_do_hot_reload;
230 23 100 66     74 unless ( $self->getOption('no-pid-file') or $hot_reload ) {
231 19         436 $pid = File::Flock::Tiny->trylock( $self->pid_file );
232 19 100       1551 unless ($pid) {
233 4 50       36 if ( $self->can_do_hot_reload ) {
234 0     0   0 chomp( my $pid = try { my $fh = path( $self->pid_file )->openr; <$fh>; } );
  0         0  
  0         0  
235 0 0 0     0 if ( $pid and kill USR2 => $pid ) {
236 0 0       0 warn("Daemon is alredy running, initiated hot reload") unless $self->getOption('no-warn');
237 0         0 exit 0;
238             }
239             else {
240 0         0 $self->error(
241             "Neither could lock pid file nor send USR2 to already running daemon.");
242             }
243             }
244             else {
245 4         116 die( "Couldn't lock "
246             . $self->pid_file
247             . ". Is another copy of this daemon already running?" );
248             }
249             }
250             }
251              
252 19         221 $SIG{PIPE} = 'IGNORE';
253 19         24 foreach my $signal ( @{ $self->shutdown_signals } ) {
  19         522  
254 57     3   476 $SIG{$signal} = sub { App::Base::Daemon::_signal_shutdown( $self, @_ ) };
  3         1620208  
255             }
256              
257             # Daemonize unless specifically asked not to.
258 19 100 66     157 unless ( $self->getOption('no-fork') or $hot_reload ) {
259 15         17715 my $child_pid = fork();
260 15 50       511 if ( !defined($child_pid) ) {
    100          
261 0         0 die("Can't fork child process: $!");
262             }
263             elsif ( $child_pid == 0 ) {
264 7         526 POSIX::setsid();
265 7         5453 my $grandchild_pid = fork();
266 7 50       365 if ( !defined($grandchild_pid) ) {
    100          
267 0         0 die("Can't fork grandchild process: $!");
268             }
269             elsif ( $grandchild_pid != 0 ) {
270 4 50       352 $pid->close if $pid;
271 4         301 exit 0;
272             }
273             else {
274             # close all STD* files, and redirect STD* to /dev/null
275 3         78 for ( 0 .. 2 ) {
276 9 50 33     534 POSIX::close($_) unless $pid and $_ == $pid->fileno;
277             }
278 3 50 33     342 ( open( STDIN, '<', '/dev/null' )
      33        
279             and open( STDOUT, '>', '/dev/null' )
280             and open( STDERR, '>', '/dev/null' ) )
281             or die "Couldn't open /dev/null: $!";
282             }
283             }
284             else {
285 8         4125730 waitpid( $child_pid, 0 );
286 8 50       547 $pid->close if $pid;
287 8         309 return $?;
288             }
289             }
290              
291 7 50       136 $self->_set_user_and_group unless $hot_reload;
292              
293 7 100       82 $pid->write_pid if $pid;
294              
295 7         629 my $result;
296 7     7   793 try { $result = $self->daemon_run( @{ $self->parsed_args } ); }
  7         277  
297             catch {
298 0     0   0 $self->error($_);
299 7         214 };
300              
301 4         92 undef $pid;
302              
303 4         8 return $result;
304             }
305              
306             sub _set_user_and_group {
307 7     7   29 my $self = shift;
308              
309 7   33     97 my $user = $self->getOption('user') // $self->user;
310 7   33     42 my $group = $self->getOption('group') // $self->group;
311 7 50 33     140 if ( $user or $group ) {
312 0 0       0 if ( $> == 0 ) {
313 0         0 my ( $uid, $gid ) = ( 0, 0 );
314 0 0       0 if ($group) {
315 0 0       0 $gid = getgrnam($group) or $self->error("Can't find group $group");
316             }
317 0 0       0 if ($user) {
318 0 0       0 $uid = getpwnam($user) or $self->error("Can't find user $user");
319             }
320 0 0 0     0 if ( $uid or $gid ) {
321 0         0 chown $uid, $gid, $self->pid_file;
322             }
323 0 0       0 if ($gid) {
324 0         0 POSIX::setgid($gid);
325             }
326 0 0       0 if ($uid) {
327 0         0 POSIX::setuid($uid);
328             }
329             }
330             else {
331 0 0       0 warn("Not running as root, can't setuid/setgid") unless $self->getOption('no-warn');
332             }
333             }
334              
335 7         12 return;
336             }
337              
338             =head2 error
339              
340             Handles the output of errors, including shutting down the running daemon by
341             calling handle_shutdown(). If you have a serious problem that should NOT
342             result in shutting down your daemon, use warn() instead.
343              
344             =cut
345              
346             sub error {
347 6     6 1 54 my $self = shift;
348 6 50       18 warn( "Shutting down: " . join( ' ', @_ ) ) unless $self->getOption('no-warn');
349              
350 6         264 $self->handle_shutdown();
351 6         1692 return exit(-1);
352             }
353              
354 9     9   339 no Moose::Role;
  9         21  
  9         63  
355             1;
356              
357             __END__
358              
359             =head1 USAGE
360              
361             =head2 Inheritance
362              
363             Invocation of a App::Base::Daemon-based daemon is accomplished as follows:
364              
365             =over 4
366              
367             =item -
368              
369             Define a class that implements App::Base::Daemon
370              
371             =item -
372              
373             Instantiate an object of that class via new()
374              
375             =item -
376              
377             Run the daemon by calling run(). The return value of run() is the exit
378             status of the daemon, and should typically be passed back to the calling
379             program via exit()
380              
381             =back
382              
383             =head2 The new() method
384              
385             (See App::Base::Script::Common::new)
386              
387             =head2 Options handling
388              
389             (See App::Base::Script::Common, "Options handling")
390              
391             =head1 LICENSE AND COPYRIGHT
392              
393             Copyright (C) 2010-2014 Binary.com
394              
395             This program is free software; you can redistribute it and/or modify it
396             under the terms of either: the GNU General Public License as published
397             by the Free Software Foundation; or the Artistic License.
398              
399             See http://dev.perl.org/licenses/ for more information.
400              
401             =cut