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