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