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