Adding PidFile implementation and making Daemonize use that
[gitmo/MooseX-Daemonize.git] / lib / MooseX / Daemonize.pm
CommitLineData
a4952679 1package MooseX::Daemonize;
a392fa53 2use strict; # because Kwalitee is pedantic
a4952679 3use Moose::Role;
2eced271 4use MooseX::Types::Path::Class;
5use Moose::Util::TypeConstraints;
a4952679 6
2eced271 7our $VERSION = 0.05;
a392fa53 8
2eced271 9use Carp 'carp';
10use Proc::Daemon;
11use MooseX::Daemonize::PidFile;
a4952679 12
13with qw(MooseX::Getopt);
14
15has progname => (
a392fa53 16 isa => 'Str',
17 is => 'ro',
18 lazy => 1,
19 required => 1,
20 default => sub {
21 ( my $name = lc $_[0]->meta->name ) =~ s/::/_/g;
22 return $name;
23 },
a4952679 24);
25
24a6a660 26has basedir => (
2eced271 27 isa => 'Path::Class::Dir',
cbff8e52 28 is => 'ro',
2eced271 29 coerce => 1,
cbff8e52 30 required => 1,
31 lazy => 1,
2eced271 32 default => sub { Path::Class::Dir->new('/') },
24a6a660 33);
34
a4952679 35has pidbase => (
2eced271 36 isa => 'Path::Class::Dir',
cbff8e52 37 is => 'ro',
2eced271 38 coerce => 1,
39 required => 1,
cbff8e52 40 lazy => 1,
2eced271 41 default => sub { Path::Class::Dir->new('var', 'run') },
a4952679 42);
43
2eced271 44coerce 'MooseX::Daemonize::PidFile'
45 => from 'Str'
46 => via { MooseX::Daemonize::PidFile->new( file => $_ ) };
fa2b72a4 47
a4952679 48has pidfile => (
2eced271 49 isa => 'MooseX::Daemonize::PidFile',
cbff8e52 50 is => 'rw',
51 lazy => 1,
52 required => 1,
53 coerce => 1,
54 predicate => 'has_pidfile',
55 default => sub {
fa2b72a4 56 my $file = $_[0]->pidbase . '/' . $_[0]->progname . '.pid';
2eced271 57 confess "Cannot write to $file" unless (-e $file ? -w $file : -w $_[0]->pidbase);
58 MooseX::Daemonize::PidFile->new( file => $file );
fa2b72a4 59 },
60 handles => {
61 check => 'running',
62 save_pid => 'write',
63 remove_pid => 'remove',
64 get_pid => 'pid',
3543c999 65 },
a4952679 66);
67
68has foreground => (
2eced271 69 metaclass => 'Getopt',
cbff8e52 70 cmd_aliases => 'f',
a4952679 71 isa => 'Bool',
72 is => 'ro',
73 default => sub { 0 },
74);
75
cbff8e52 76has is_daemon => (
77 isa => 'Bool',
78 is => 'rw',
79 default => sub { 0 },
80);
81
b916501e 82has stop_timeout => (
83 isa => 'Int',
84 is => 'rw',
2eced271 85 default => sub { 2 }
b916501e 86);
87
a392fa53 88sub daemonize {
89 my ($self) = @_;
cbff8e52 90 return if Proc::Daemon::Fork;
a392fa53 91 Proc::Daemon::Init;
cbff8e52 92 $self->is_daemon(1);
a392fa53 93}
94
a4952679 95sub start {
96 my ($self) = @_;
2eced271 97
cbff8e52 98 confess "instance already running" if $self->check;
2eced271 99
a4952679 100 $self->daemonize unless $self->foreground;
101
cbff8e52 102 return unless $self->is_daemon;
103
104 $self->pidfile->pid($$);
105
3543c999 106 # Avoid 'stdin reopened for output' warning with newer perls
0585edbb 107 ## no critic
3543c999 108 open( NULL, '/dev/null' );
109 <NULL> if (0);
0585edbb 110 ## use critic
fa2b72a4 111
24a6a660 112 # Change to basedir
113 chdir $self->basedir;
fa2b72a4 114
3543c999 115 $self->save_pid;
116 $self->setup_signals;
117 return $$;
118}
119
b916501e 120# Make _kill *really* private
121my $_kill;
122
3543c999 123sub stop {
124 my ( $self, %args ) = @_;
125 my $pid = $self->get_pid;
b916501e 126 $self->$_kill($pid) unless $self->foreground();
3543c999 127 $self->remove_pid;
128 return 1 if $args{no_exit};
129 exit;
130}
131
a4952679 132sub restart {
133 my ($self) = @_;
7ada91b8 134 $self->stop( no_exit => 1 );
a4952679 135 $self->start();
136}
137
a4952679 138sub setup_signals {
3543c999 139 my ($self) = @_;
140 $SIG{INT} = sub { $self->handle_sigint; };
141 $SIG{HUP} = sub { $self->handle_sighup };
a4952679 142}
143
2361a590 144sub handle_sigint { $_[0]->stop; }
145sub handle_sighup { $_[0]->restart; }
a4952679 146
b916501e 147$_kill = sub {
a4952679 148 my ( $self, $pid ) = @_;
2361a590 149 return unless $pid;
3543c999 150 unless ( CORE::kill 0 => $pid ) {
151
152 # warn "$pid already appears dead.";
153 return;
154 }
155
156 if ( $pid eq $$ ) {
157
b916501e 158 # warn "$pid is us! Can't commit suicide.";
a4952679 159 return;
160 }
161
b916501e 162 my $timeout = $self->stop_timeout;
a4952679 163
b916501e 164 # kill 0 => $pid returns 0 if the process is dead
165 # $!{EPERM} could also be true if we cant kill it (permission error)
a4952679 166
b916501e 167 # Try SIGINT ... 2s ... SIGTERM ... 2s ... SIGKILL ... 3s ... UNDEAD!
168 for ( [ 2, $timeout ], [15, $timeout], [9, $timeout * 1.5] ) {
169 my ($signal, $timeout) = @$_;
170 $timeout = int $timeout;
171
172 CORE::kill($signal, $pid);
173
174 last unless CORE::kill 0 => $pid or $!{EPERM};
a4952679 175
b916501e 176 while ($timeout) {
177 sleep(1);
178 last unless CORE::kill 0 => $pid or $!{EPERM};
179 $timeout--;
180 }
a4952679 181 }
182
b916501e 183 return unless ( CORE::kill 0 => $pid or $!{EPERM} );
184
185 # IF it is still running
186 carp "$pid doesn't seem to want to die."; # AHH EVIL DEAD!
187};
a4952679 188
1891;
190__END__
191
a4952679 192=head1 NAME
193
b916501e 194MooseX::Daemonize - provides a Role that daemonizes your Moose based
195application.
a4952679 196
197=head1 VERSION
198
b916501e 199This document describes MooseX::Daemonize version 0.04
a4952679 200
201=head1 SYNOPSIS
202
203 package FileMaker;
204 use Moose;
205 with qw(MooseX::Daemonize);
206
207 sub create_file {
208 my ( $self, $file ) = @_;
209 open( FILE, ">$file" ) || die;
210 close(FILE);
211 }
212
213 no Moose;
214
215 # then in the main package ...
216
217 my $daemon = FileMaker->new();
218 $daemon->start();
219 $daemon->create_file($file);
220 $daemon->stop();
221
222=head1 DESCRIPTION
223
b916501e 224Often you want to write a persistant daemon that has a pid file, and responds
225appropriately to Signals. This module helps provide the basic infrastructure
226to do that.
a4952679 227
228=head1 ATTRIBUTES
229
230=over
231
2eced271 232=item progname Path::Class::Dir | Str
a4952679 233
cbff8e52 234The name of our daemon, defaults to $self->meta->name =~ s/::/_/;
a4952679 235
2eced271 236=item pidbase Path::Class::Dir | Str
a4952679 237
238The base for our bid, defaults to /var/run/$progname
239
2eced271 240=item pidfile MooseX::Daemonize::PidFile | Str
a4952679 241
2eced271 242The file we store our PID in, defaults to /var/run/$progname
a4952679 243
244=item foreground Bool
245
b916501e 246If true, the process won't background. Useful for debugging. This option can
247be set via Getopt's -f.
a4952679 248
e7a196e7 249=item is_daemon Bool
250
b916501e 251If true, the process is the backgrounded process. This is useful for example
252in an after 'start' => sub { } block
253
254=item stop_timeout
255
256Number of seconds to wait for the process to stop, before trying harder to kill
257it. Defaults to 2 seconds
e7a196e7 258
a4952679 259=back
260
261=head1 METHODS
262
263=over
264
265=item check()
266
267Check to see if an instance is already running.
268
269=item start()
270
271Setup a pidfile, fork, then setup the signal handlers.
272
273=item stop()
274
275Stop the process matching the pidfile, and unlinks the pidfile.
276
277=item restart()
278
279Litterally
280
281 $self->stop();
282 $self->start();
283
284=item daemonize()
285
286Calls C<Proc::Daemon::Init> to daemonize this process.
287
a4952679 288=item setup_signals()
289
290Setup the signal handlers, by default it only sets up handlers for SIGINT and SIGHUP
291
292=item handle_sigint()
293
cecbee2d 294Handle a INT signal, by default calls C<$self->stop()>
a4952679 295
296=item handle_sighup()
297
cecbee2d 298Handle a HUP signal. By default calls C<$self->restart()>
a4952679 299
3543c999 300=item get_pid
301
cecbee2d 302Lookup the pid from our pidfile.
303
3543c999 304=item save_pid
305
cecbee2d 306Save the current pid in our pidfile
307
3543c999 308=item remove_pid
309
cecbee2d 310Delete our pidfile
311
a4952679 312=item meta()
313
cecbee2d 314The C<meta()> method from L<Class::MOP::Class>
a4952679 315
316=back
317
318=head1 DEPENDENCIES
319
320=for author to fill in:
321 A list of all the other modules that this module relies upon,
322 including any restrictions on versions, and an indication whether
323 the module is part of the standard Perl distribution, part of the
324 module's distribution, or must be installed separately. ]
325
2eced271 326Obviously L<Moose>, and L<Proc::Daemon>
a4952679 327
328=head1 INCOMPATIBILITIES
329
330=for author to fill in:
331 A list of any modules that this module cannot be used in conjunction
332 with. This may be due to name conflicts in the interface, or
333 competition for system or program resources, or due to internal
334 limitations of Perl (for example, many modules that use source code
335 filters are mutually incompatible).
336
337None reported.
338
339
340=head1 BUGS AND LIMITATIONS
341
342=for author to fill in:
343 A list of known problems with the module, together with some
344 indication Whether they are likely to be fixed in an upcoming
345 release. Also a list of restrictions on the features the module
346 does provide: data types that cannot be handled, performance issues
347 and the circumstances in which they may arise, practical
348 limitations on the size of data sets, special cases that are not
349 (yet) handled, etc.
350
351No bugs have been reported.
352
353Please report any bugs or feature requests to
354C<bug-acme-dahut-call@rt.cpan.org>, or through the web interface at
355L<http://rt.cpan.org>.
356
357=head1 SEE ALSO
358
359L<Proc::Daemon>, L<Daemon::Generic>, L<MooseX::Getopt>
360
361=head1 AUTHOR
362
363Chris Prather C<< <perigrin@cpan.org> >>
364
7ada91b8 365=head1 THANKS
366
b916501e 367Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the
368#moose denzians
a4952679 369
637573c4 370Some bug fixes sponsored by Takkle Inc.
371
a4952679 372=head1 LICENCE AND COPYRIGHT
373
b916501e 374Copyright (c) 2007, Chris Prather C<< <perigrin@cpan.org> >>. All rights
375reserved.
a4952679 376
377This module is free software; you can redistribute it and/or
378modify it under the same terms as Perl itself. See L<perlartistic>.
379
380
381=head1 DISCLAIMER OF WARRANTY
382
383BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
384FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
385OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
386PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
387EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
388WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
389ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
390YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
391NECESSARY SERVICING, REPAIR, OR CORRECTION.
392
393IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
394WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
395REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
396LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
397OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
398THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
399RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
400FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
401SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
402SUCH DAMAGES.