File Coverage

blib/lib/App/Base/Daemon.pm
Criterion Covered Total %
statement 84 107 78.5
branch 23 56 41.0
condition 13 35 37.1
subroutine 18 20 90.0
pod 2 2 100.0
total 140 220 63.6


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