adding some overloading
[gitmo/MooseX-Daemonize.git] / lib / MooseX / Daemonize.pm
CommitLineData
a4952679 1package MooseX::Daemonize;
a392fa53 2use strict; # because Kwalitee is pedantic
a4952679 3use Moose::Role;
8ac4733f 4use MooseX::Types::Path::Class;
a392fa53 5
d9e417f4 6our $VERSION = 0.05;
a4952679 7
4327fe98 8with 'MooseX::Daemonize::WithPidFile',
9 'MooseX::Getopt';
a4952679 10
11has progname => (
4327fe98 12 metaclass => 'Getopt',
13 isa => 'Str',
14 is => 'ro',
15 lazy => 1,
16 required => 1,
17 default => sub {
a392fa53 18 ( my $name = lc $_[0]->meta->name ) =~ s/::/_/g;
19 return $name;
20 },
a4952679 21);
22
d9e417f4 23has pidbase => (
4327fe98 24 metaclass => 'Getopt',
25 isa => 'Path::Class::Dir',
26 is => 'ro',
27 coerce => 1,
28 required => 1,
29 lazy => 1,
30 default => sub { Path::Class::Dir->new('var', 'run') },
24a6a660 31);
32
d9e417f4 33has basedir => (
4327fe98 34 metaclass => 'Getopt',
35 isa => 'Path::Class::Dir',
36 is => 'ro',
37 coerce => 1,
38 required => 1,
39 lazy => 1,
40 default => sub { Path::Class::Dir->new('/') },
a4952679 41);
42
43has foreground => (
2eced271 44 metaclass => 'Getopt',
cbff8e52 45 cmd_aliases => 'f',
a4952679 46 isa => 'Bool',
47 is => 'ro',
48 default => sub { 0 },
49);
50
b916501e 51has stop_timeout => (
4327fe98 52 metaclass => 'Getopt',
53 isa => 'Int',
54 is => 'rw',
55 default => sub { 2 }
b916501e 56);
57
4327fe98 58# methods ...
59
60## PID file related stuff ...
61
d9e417f4 62sub init_pidfile {
63 my $self = shift;
64 my $file = $self->pidbase . '/' . $self->progname . '.pid';
65 confess "Cannot write to $file" unless (-e $file ? -w $file : -w $self->pidbase);
d8985b7d 66 MooseX::Daemonize::Pid::File->new( file => $file );
d9e417f4 67}
68
4327fe98 69# backwards compat,
70sub check { (shift)->pidfile->is_running }
71sub save_pid { (shift)->pidfile->write }
72sub remove_pid { (shift)->pidfile->remove }
73sub get_pid { (shift)->pidfile->pid }
74
75## signal handling ...
76
77sub setup_signals {
78 my $self = shift;
79 $SIG{'INT'} = sub { $self->handle_sigint };
80 $SIG{'HUP'} = sub { $self->handle_sighup };
81}
82
83sub handle_sigint { $_[0]->stop; }
84sub handle_sighup { $_[0]->restart; }
85
86## daemon control methods ...
87
a4952679 88sub start {
89 my ($self) = @_;
2eced271 90
d8985b7d 91 confess "instance already running" if $self->pidfile->is_running;
2eced271 92
a4952679 93 $self->daemonize unless $self->foreground;
8ac4733f 94
cbff8e52 95 return unless $self->is_daemon;
96
4327fe98 97 $self->pidfile->pid($$);
cbff8e52 98
24a6a660 99 # Change to basedir
100 chdir $self->basedir;
fa2b72a4 101
ff5cee29 102 $self->pidfile->write;
3543c999 103 $self->setup_signals;
104 return $$;
105}
106
4327fe98 107sub restart {
108 my ($self) = @_;
109 $self->stop( no_exit => 1 );
110 $self->start();
111}
112
b916501e 113# Make _kill *really* private
114my $_kill;
115
3543c999 116sub stop {
117 my ( $self, %args ) = @_;
ff5cee29 118 my $pid = $self->pidfile->pid;
b916501e 119 $self->$_kill($pid) unless $self->foreground();
ff5cee29 120 $self->pidfile->remove;
3543c999 121 return 1 if $args{no_exit};
122 exit;
123}
124
b916501e 125$_kill = sub {
a4952679 126 my ( $self, $pid ) = @_;
2361a590 127 return unless $pid;
3543c999 128 unless ( CORE::kill 0 => $pid ) {
129
130 # warn "$pid already appears dead.";
131 return;
132 }
133
134 if ( $pid eq $$ ) {
135
b916501e 136 # warn "$pid is us! Can't commit suicide.";
a4952679 137 return;
138 }
139
b916501e 140 my $timeout = $self->stop_timeout;
a4952679 141
b916501e 142 # kill 0 => $pid returns 0 if the process is dead
143 # $!{EPERM} could also be true if we cant kill it (permission error)
a4952679 144
b916501e 145 # Try SIGINT ... 2s ... SIGTERM ... 2s ... SIGKILL ... 3s ... UNDEAD!
146 for ( [ 2, $timeout ], [15, $timeout], [9, $timeout * 1.5] ) {
ea9485d8 147 my ($signal, $timeout) = @$_;
148 $timeout = int $timeout;
149
150 CORE::kill($signal, $pid);
151
b916501e 152 last unless CORE::kill 0 => $pid or $!{EPERM};
ea9485d8 153
154 while ($timeout) {
155 sleep(1);
156 last unless CORE::kill 0 => $pid or $!{EPERM};
157 $timeout--;
158 }
a4952679 159 }
160
b916501e 161 return unless ( CORE::kill 0 => $pid or $!{EPERM} );
162
163 # IF it is still running
d9e417f4 164 Carp::carp "$pid doesn't seem to want to die."; # AHH EVIL DEAD!
b916501e 165};
a4952679 166
1671;
168__END__
169
8ac4733f 170=pod
171
a4952679 172=head1 NAME
173
b916501e 174MooseX::Daemonize - provides a Role that daemonizes your Moose based
175application.
a4952679 176
177=head1 VERSION
178
4327fe98 179This document describes MooseX::Daemonize version 0.05
a4952679 180
181=head1 SYNOPSIS
182
4327fe98 183 package My::Daemon;
a4952679 184 use Moose;
4327fe98 185
a4952679 186 with qw(MooseX::Daemonize);
4327fe98 187
188 # ... define your class ....
189
190 after start => sub {
191 my $self = shift;
192 return unless $self->is_daemon;
193 # your daemon code here ...
194 };
a4952679 195
4327fe98 196 # then in your script ...
197
198 my $daemon = My::Daemon->new_with_options();
a4952679 199
4327fe98 200 my ($command) = @{$daemon->extra_argv}
201 defined $command || die "No command specified";
202
203 $daemon->start() if $command eq 'start';
204 $daemon->stop() if $command eq 'stop';
a4952679 205
206=head1 DESCRIPTION
207
b916501e 208Often you want to write a persistant daemon that has a pid file, and responds
4327fe98 209appropriately to Signals. This module provides a set of basic roles as an
210infrastructure to do that.
a4952679 211
212=head1 ATTRIBUTES
213
4327fe98 214This list includes attributes brought in from other roles as well
215we include them here for ease of documentation. All of these attributes
216are settable though L<MooseX::Getopt>'s command line handling, with the
217exception of C<is_daemon>.
218
a4952679 219=over
220
4327fe98 221=item I<progname Path::Class::Dir | Str>
a4952679 222
4327fe98 223The name of our daemon, defaults to C<$package_name =~ s/::/_/>;
a4952679 224
4327fe98 225=item I<pidbase Path::Class::Dir | Str>
a4952679 226
4327fe98 227The base for our bid, defaults to C</var/run/$progname>
a4952679 228
4327fe98 229=item I<pidfile MooseX::Daemonize::Pid::File | Str>
a4952679 230
4327fe98 231The file we store our PID in, defaults to C</var/run/$progname>
a4952679 232
4327fe98 233=item I<foreground Bool>
a4952679 234
b916501e 235If true, the process won't background. Useful for debugging. This option can
236be set via Getopt's -f.
a4952679 237
4327fe98 238=item I<is_daemon Bool>
239
240If true, the process is the backgrounded daemon process, if false it is the
241parent process. This is useful for example in an C<after 'start' => sub { }>
242block.
e7a196e7 243
4327fe98 244B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>.
b916501e 245
4327fe98 246=item I<stop_timeout>
b916501e 247
248Number of seconds to wait for the process to stop, before trying harder to kill
4327fe98 249it. Defaults to 2 seconds.
e7a196e7 250
a4952679 251=back
252
253=head1 METHODS
254
4327fe98 255=head2 Daemon Control Methods
256
257These methods can be used to control the daemon behavior. Every effort
258has been made to have these methods DWIM (Do What I Mean), so that you
259can focus on just writing the code for your daemon.
a4952679 260
4327fe98 261Extending these methods is best done with the L<Moose> method modifiers,
262such as C<before>, C<after> and C<around>.
263
264=over 4
265
266=item B<start>
a4952679 267
268Setup a pidfile, fork, then setup the signal handlers.
269
4327fe98 270=item B<stop>
a4952679 271
272Stop the process matching the pidfile, and unlinks the pidfile.
273
4327fe98 274=item B<restart>
a4952679 275
4327fe98 276Literally this is:
a4952679 277
278 $self->stop();
279 $self->start();
280
4327fe98 281=back
282
283=head2 Pidfile Handling Methods
284
285=over 4
286
287=item B<init_pidfile>
288
289This method will create a L<MooseX::Daemonize::Pid::File> object and tell
290it to store the PID in the file C<$pidbase/$progname.pid>.
291
292=item B<check>
293
294This checks to see if the daemon process is currently running by checking
295the pidfile.
a4952679 296
4327fe98 297=item B<get_pid>
a4952679 298
4327fe98 299Returns the PID of the daemon process.
a4952679 300
4327fe98 301=item B<save_pid>
a4952679 302
4327fe98 303Write the pidfile.
304
305=item B<remove_pid>
306
307Removes the pidfile.
308
309=back
310
311=head2 Signal Handling Methods
312
313=over 4
314
315=item B<setup_signals>
316
317Setup the signal handlers, by default it only sets up handlers for SIGINT and
318SIGHUP. If you wish to add more signals just use the C<after> method modifier
319and add them.
320
321=item B<handle_sigint>
a4952679 322
cecbee2d 323Handle a INT signal, by default calls C<$self->stop()>
a4952679 324
4327fe98 325=item B<handle_sighup>
a4952679 326
cecbee2d 327Handle a HUP signal. By default calls C<$self->restart()>
a4952679 328
4327fe98 329=back
330
331=head2 Introspection
332
333=over 4
334
a4952679 335=item meta()
336
cecbee2d 337The C<meta()> method from L<Class::MOP::Class>
a4952679 338
339=back
340
341=head1 DEPENDENCIES
342
4327fe98 343L<Moose>, L<MooseX::Getopt>, L<MooseX::Types::Path::Class> and L<POSIX>
a4952679 344
345=head1 INCOMPATIBILITIES
346
4327fe98 347None reported. Although obviously this will not work on Windows.
a4952679 348
349=head1 BUGS AND LIMITATIONS
350
a4952679 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
4327fe98 359L<Proc::Daemon>, L<Daemon::Generic>
a4952679 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
a4952679 380=head1 DISCLAIMER OF WARRANTY
381
382BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
383FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
384OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
385PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
386EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
387WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
388ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
389YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
390NECESSARY SERVICING, REPAIR, OR CORRECTION.
391
392IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
393WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
394REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
395LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
396OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
397THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
398RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
399FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
400SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
401SUCH DAMAGES.
8ac4733f 402
403=cut