0.10 release
[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
1d85c76d 6our $VERSION = "0.10";
a4952679 7
4327fe98 8with 'MooseX::Daemonize::WithPidFile',
9 'MooseX::Getopt';
1d85c76d 10
df3c463b 11sub OK () { 0 }
12sub ERROR () { 1 }
a4952679 13
14has progname => (
5b9ebe08 15 metaclass => 'Getopt',
4327fe98 16 isa => 'Str',
17 is => 'ro',
18 lazy => 1,
19 required => 1,
20 default => sub {
a392fa53 21 ( my $name = lc $_[0]->meta->name ) =~ s/::/_/g;
22 return $name;
23 },
a4952679 24);
25
d9e417f4 26has pidbase => (
5b9ebe08 27 metaclass => 'Getopt',
4327fe98 28 isa => 'Path::Class::Dir',
29 is => 'ro',
30 coerce => 1,
5b9ebe08 31 required => 1,
4327fe98 32 lazy => 1,
b38ab84f 33 default => sub { Path::Class::Dir->new('', 'var', 'run') },
24a6a660 34);
35
d9e417f4 36has basedir => (
5b9ebe08 37 metaclass => 'Getopt',
4327fe98 38 isa => 'Path::Class::Dir',
39 is => 'ro',
40 coerce => 1,
41 required => 1,
42 lazy => 1,
43 default => sub { Path::Class::Dir->new('/') },
a4952679 44);
45
46has foreground => (
2eced271 47 metaclass => 'Getopt',
cbff8e52 48 cmd_aliases => 'f',
a4952679 49 isa => 'Bool',
50 is => 'ro',
51 default => sub { 0 },
52);
53
b916501e 54has stop_timeout => (
5b9ebe08 55 metaclass => 'Getopt',
4327fe98 56 isa => 'Int',
57 is => 'rw',
58 default => sub { 2 }
b916501e 59);
60
5b9ebe08 61# internal book-keeping
62
63has status_message => (
64 metaclass => 'NoGetopt',
65 isa => 'Str',
66 is => 'rw',
67 clearer => 'clear_status_message',
68);
69
70has exit_code => (
71 metaclass => 'NoGetopt',
72 isa => 'Int',
73 is => 'rw',
74 clearer => 'clear_exit_code',
75);
76
4327fe98 77# methods ...
78
79## PID file related stuff ...
80
d9e417f4 81sub init_pidfile {
82 my $self = shift;
83 my $file = $self->pidbase . '/' . $self->progname . '.pid';
84 confess "Cannot write to $file" unless (-e $file ? -w $file : -w $self->pidbase);
d8985b7d 85 MooseX::Daemonize::Pid::File->new( file => $file );
d9e417f4 86}
87
5b9ebe08 88# backwards compat,
4327fe98 89sub check { (shift)->pidfile->is_running }
90sub save_pid { (shift)->pidfile->write }
91sub remove_pid { (shift)->pidfile->remove }
92sub get_pid { (shift)->pidfile->pid }
93
94## signal handling ...
95
96sub setup_signals {
97 my $self = shift;
4a24225a 98 $SIG{'INT'} = sub { $self->shutdown };
99# I can't think of a sane default here really ...
100# $SIG{'HUP'} = sub { $self->handle_sighup };
4327fe98 101}
102
4a24225a 103sub shutdown {
104 my $self = shift;
105 $self->pidfile->remove if $self->pidfile->pid == $$;
106 exit(0);
107}
4327fe98 108
109## daemon control methods ...
110
a4952679 111sub start {
5b9ebe08 112 my $self = shift;
113
114 $self->clear_status_message;
115 $self->clear_exit_code;
116
117 if ($self->pidfile->is_running) {
9c8a33b3 118 $self->exit_code($self->OK);
1d85c76d 119 $self->status_message('Daemon is already running with pid (' . $self->pidfile->pid . ')');
5b9ebe08 120 return !($self->exit_code);
121 }
1d85c76d 122
123 if ($self->foreground) {
5b9ebe08 124 $self->is_daemon(1);
125 }
1d85c76d 126 else {
127 eval { $self->daemonize };
5b9ebe08 128 if ($@) {
9c8a33b3 129 $self->exit_code($self->ERROR);
5b9ebe08 130 $self->status_message('Start failed : ' . $@);
131 return !($self->exit_code);
132 }
133 }
cbff8e52 134
5b9ebe08 135 unless ($self->is_daemon) {
1d85c76d 136 $self->exit_code($self->OK);
5b9ebe08 137 $self->status_message('Start succeeded');
138 return !($self->exit_code);
139 }
140
141 $self->pidfile->pid($$);
cbff8e52 142
24a6a660 143 # Change to basedir
144 chdir $self->basedir;
fa2b72a4 145
ff5cee29 146 $self->pidfile->write;
3543c999 147 $self->setup_signals;
148 return $$;
149}
150
5b9ebe08 151sub status {
152 my $self = shift;
153
154 $self->clear_status_message;
155 $self->clear_exit_code;
156
157 if ($self->pidfile->is_running) {
1d85c76d 158 $self->exit_code($self->OK);
159 $self->status_message('Daemon is running with pid (' . $self->pidfile->pid . ')');
5b9ebe08 160 }
1d85c76d 161 else {
9c8a33b3 162 $self->exit_code($self->ERROR);
5b9ebe08 163 $self->status_message('Daemon is not running with pid (' . $self->pidfile->pid . ')');
164 }
165
166 return !($self->exit_code);
167}
168
4327fe98 169sub restart {
5b9ebe08 170 my $self = shift;
171
172 $self->clear_status_message;
173 $self->clear_exit_code;
174
175 unless ($self->stop) {
9c8a33b3 176 $self->exit_code($self->ERROR);
5b9ebe08 177 $self->status_message('Restart (Stop) failed : ' . $@);
178 }
179
180 unless ($self->start) {
9c8a33b3 181 $self->exit_code($self->ERROR);
5b9ebe08 182 $self->status_message('Restart (Start) failed : ' . $@);
183 }
184
9c8a33b3 185 if ($self->exit_code == $self->OK) {
186 $self->exit_code($self->OK);
92cf56b7 187 $self->status_message("Restart successful");
188 }
5b9ebe08 189
190 return !($self->exit_code);
4327fe98 191}
192
b916501e 193# Make _kill *really* private
194my $_kill;
195
3543c999 196sub stop {
5b9ebe08 197 my $self = shift;
198
199 $self->clear_status_message;
200 $self->clear_exit_code;
201
202 # if the pid is not running
203 # then we dont need to stop
204 # anything ...
205 if ($self->pidfile->is_running) {
206
207 # if we are foreground, then
208 # no need to try and kill
209 # ourselves
210 unless ($self->foreground) {
211
212 # kill the process ...
213 eval { $self->$_kill($self->pidfile->pid) };
214 # and complain if we can't ...
215 if ($@) {
9c8a33b3 216 $self->exit_code($self->ERROR);
5b9ebe08 217 $self->status_message('Stop failed : ' . $@);
218 }
219 # or gloat if we succeed ..
220 else {
9c8a33b3 221 $self->exit_code($self->OK);
5b9ebe08 222 $self->status_message('Stop succeeded');
223 }
224
225 }
5b9ebe08 226 }
227 else {
228 # this just returns the OK
229 # exit code for now, but
230 # we should make this overridable
1d85c76d 231 $self->exit_code($self->OK);
5b9ebe08 232 $self->status_message("Not running");
233 }
234
235 # if we are returning to our script
236 # then we actually need the opposite
237 # of what the system/OS expects
238 return !($self->exit_code);
3543c999 239}
240
b916501e 241$_kill = sub {
a4952679 242 my ( $self, $pid ) = @_;
2361a590 243 return unless $pid;
3543c999 244 unless ( CORE::kill 0 => $pid ) {
3543c999 245 # warn "$pid already appears dead.";
246 return;
247 }
248
249 if ( $pid eq $$ ) {
4a24225a 250 die "$pid is us! Can't commit suicide.";
a4952679 251 }
252
b916501e 253 my $timeout = $self->stop_timeout;
a4952679 254
b916501e 255 # kill 0 => $pid returns 0 if the process is dead
256 # $!{EPERM} could also be true if we cant kill it (permission error)
a4952679 257
b916501e 258 # Try SIGINT ... 2s ... SIGTERM ... 2s ... SIGKILL ... 3s ... UNDEAD!
b6b69aca 259 my $terminating_signal;
b916501e 260 for ( [ 2, $timeout ], [15, $timeout], [9, $timeout * 1.5] ) {
ea9485d8 261 my ($signal, $timeout) = @$_;
262 $timeout = int $timeout;
5b9ebe08 263
ea9485d8 264 CORE::kill($signal, $pid);
5b9ebe08 265
ea9485d8 266 while ($timeout) {
b6b69aca 267 unless(CORE::kill 0 => $pid or $!{EPERM}) {
268 $terminating_signal = $signal;
269 last;
270 }
ea9485d8 271 $timeout--;
b6b69aca 272 sleep(1) if $timeout;
ea9485d8 273 }
b6b69aca 274
275 last if $terminating_signal;
a4952679 276 }
277
b6b69aca 278 if($terminating_signal) {
279 if($terminating_signal == 9) {
280 # clean up the pidfile ourselves iff we used -9 and it worked
281 warn "Had to resort to 'kill -9' and it worked, wiping pidfile";
282 eval { $self->pidfile->remove };
283 if ($@) {
284 warn "Could not remove pidfile ("
285 . $self->pidfile->file
286 . ") because : $!";
287 }
288 }
289 return;
290 }
b916501e 291
292 # IF it is still running
d9e417f4 293 Carp::carp "$pid doesn't seem to want to die."; # AHH EVIL DEAD!
b916501e 294};
a4952679 295
2961;
297__END__
298
8ac4733f 299=pod
300
a4952679 301=head1 NAME
302
892a51e6 303MooseX::Daemonize - Role for daemonizing your Moose based application
a4952679 304
305=head1 VERSION
306
4327fe98 307This document describes MooseX::Daemonize version 0.05
a4952679 308
309=head1 SYNOPSIS
310
4327fe98 311 package My::Daemon;
a4952679 312 use Moose;
5b9ebe08 313
a4952679 314 with qw(MooseX::Daemonize);
5b9ebe08 315
4327fe98 316 # ... define your class ....
5b9ebe08 317
318 after start => sub {
4327fe98 319 my $self = shift;
320 return unless $self->is_daemon;
321 # your daemon code here ...
322 };
a4952679 323
5b9ebe08 324 # then in your script ...
325
4327fe98 326 my $daemon = My::Daemon->new_with_options();
5b9ebe08 327
4327fe98 328 my ($command) = @{$daemon->extra_argv}
329 defined $command || die "No command specified";
5b9ebe08 330
331 $daemon->start if $command eq 'start';
332 $daemon->status if $command eq 'status';
333 $daemon->restart if $command eq 'restart';
334 $daemon->stop if $command eq 'stop';
335
92cf56b7 336 warn($daemon->status_message);
5b9ebe08 337 exit($daemon->exit_code);
338
a4952679 339=head1 DESCRIPTION
340
b916501e 341Often you want to write a persistant daemon that has a pid file, and responds
5b9ebe08 342appropriately to Signals. This module provides a set of basic roles as an
4327fe98 343infrastructure to do that.
a4952679 344
345=head1 ATTRIBUTES
346
4327fe98 347This list includes attributes brought in from other roles as well
348we include them here for ease of documentation. All of these attributes
5b9ebe08 349are settable though L<MooseX::Getopt>'s command line handling, with the
4327fe98 350exception of C<is_daemon>.
351
a4952679 352=over
353
4327fe98 354=item I<progname Path::Class::Dir | Str>
a4952679 355
4327fe98 356The name of our daemon, defaults to C<$package_name =~ s/::/_/>;
a4952679 357
4327fe98 358=item I<pidbase Path::Class::Dir | Str>
a4952679 359
1d85c76d 360The base for our PID, defaults to C</var/run/>
a4952679 361
4327fe98 362=item I<pidfile MooseX::Daemonize::Pid::File | Str>
a4952679 363
1d85c76d 364The file we store our PID in, defaults to C<$pidbase/$progname.pid>
a4952679 365
4327fe98 366=item I<foreground Bool>
a4952679 367
5b9ebe08 368If true, the process won't background. Useful for debugging. This option can
b916501e 369be set via Getopt's -f.
a4952679 370
4327fe98 371=item I<is_daemon Bool>
372
5b9ebe08 373If true, the process is the backgrounded daemon process, if false it is the
374parent process. This is useful for example in an C<after 'start' => sub { }>
375block.
e7a196e7 376
4327fe98 377B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>.
b916501e 378
4327fe98 379=item I<stop_timeout>
b916501e 380
381Number of seconds to wait for the process to stop, before trying harder to kill
4327fe98 382it. Defaults to 2 seconds.
e7a196e7 383
a4952679 384=back
385
5b9ebe08 386These are the internal attributes, which are not available through MooseX::Getopt.
387
388=over 4
389
390=item I<exit_code Int>
391
92cf56b7 392=item I<status_message Str>
5b9ebe08 393
394=back
395
396=head1 METHODS
a4952679 397
4327fe98 398=head2 Daemon Control Methods
399
5b9ebe08 400These methods can be used to control the daemon behavior. Every effort
401has been made to have these methods DWIM (Do What I Mean), so that you
402can focus on just writing the code for your daemon.
a4952679 403
5b9ebe08 404Extending these methods is best done with the L<Moose> method modifiers,
4327fe98 405such as C<before>, C<after> and C<around>.
406
407=over 4
408
409=item B<start>
a4952679 410
411Setup a pidfile, fork, then setup the signal handlers.
412
4327fe98 413=item B<stop>
a4952679 414
415Stop the process matching the pidfile, and unlinks the pidfile.
416
4327fe98 417=item B<restart>
a4952679 418
4327fe98 419Literally this is:
a4952679 420
421 $self->stop();
422 $self->start();
423
5b9ebe08 424=item B<status>
425
92cf56b7 426=item B<shutdown>
427
4327fe98 428=back
429
5b9ebe08 430
4327fe98 431=head2 Pidfile Handling Methods
432
433=over 4
434
435=item B<init_pidfile>
436
437This method will create a L<MooseX::Daemonize::Pid::File> object and tell
438it to store the PID in the file C<$pidbase/$progname.pid>.
439
440=item B<check>
441
5b9ebe08 442This checks to see if the daemon process is currently running by checking
4327fe98 443the pidfile.
a4952679 444
4327fe98 445=item B<get_pid>
a4952679 446
4327fe98 447Returns the PID of the daemon process.
a4952679 448
4327fe98 449=item B<save_pid>
a4952679 450
4327fe98 451Write the pidfile.
452
453=item B<remove_pid>
454
455Removes the pidfile.
456
457=back
458
459=head2 Signal Handling Methods
460
461=over 4
462
463=item B<setup_signals>
464
5b9ebe08 465Setup the signal handlers, by default it only sets up handlers for SIGINT and
4327fe98 466SIGHUP. If you wish to add more signals just use the C<after> method modifier
467and add them.
468
469=item B<handle_sigint>
a4952679 470
cecbee2d 471Handle a INT signal, by default calls C<$self->stop()>
a4952679 472
4327fe98 473=item B<handle_sighup>
a4952679 474
cecbee2d 475Handle a HUP signal. By default calls C<$self->restart()>
a4952679 476
4327fe98 477=back
478
9c8a33b3 479=head2 Exit Code Methods
480
481These are overriable constant methods used for setting the exit code.
482
483=over 4
484
485=item OK
486
487Returns 0.
488
489=item ERROR
490
491Returns 1.
492
493=back
494
4327fe98 495=head2 Introspection
496
497=over 4
498
a4952679 499=item meta()
500
cecbee2d 501The C<meta()> method from L<Class::MOP::Class>
a4952679 502
503=back
504
505=head1 DEPENDENCIES
506
4327fe98 507L<Moose>, L<MooseX::Getopt>, L<MooseX::Types::Path::Class> and L<POSIX>
a4952679 508
509=head1 INCOMPATIBILITIES
510
4327fe98 511None reported. Although obviously this will not work on Windows.
a4952679 512
513=head1 BUGS AND LIMITATIONS
514
a4952679 515No bugs have been reported.
516
517Please report any bugs or feature requests to
518C<bug-acme-dahut-call@rt.cpan.org>, or through the web interface at
519L<http://rt.cpan.org>.
520
521=head1 SEE ALSO
522
4327fe98 523L<Proc::Daemon>, L<Daemon::Generic>
a4952679 524
92cf56b7 525=head1 AUTHORS
a4952679 526
69186a48 527Chris Prather C<< <chris@prather.org >>
a4952679 528
92cf56b7 529Stevan Little C<< <stevan.little@iinteractive.com> >>
530
7ada91b8 531=head1 THANKS
532
5b9ebe08 533Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the
b916501e 534#moose denzians
a4952679 535
637573c4 536Some bug fixes sponsored by Takkle Inc.
537
a4952679 538=head1 LICENCE AND COPYRIGHT
539
1d85c76d 540Copyright (c) 2007-2010, Chris Prather C<< <chris@prather.org> >>. Some rights
b916501e 541reserved.
a4952679 542
543This module is free software; you can redistribute it and/or
544modify it under the same terms as Perl itself. See L<perlartistic>.
545
a4952679 546=head1 DISCLAIMER OF WARRANTY
547
548BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
549FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
550OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
551PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
552EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
553WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
554ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
555YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
556NECESSARY SERVICING, REPAIR, OR CORRECTION.
557
558IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
559WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
560REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
561LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
562OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
563THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
564RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
565FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
566SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
567SUCH DAMAGES.
8ac4733f 568
569=cut