whoops
[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
8625135b 6our $VERSION = '0.12';
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
2ecc2ccb 371=item I<no_double_fork Bool>
372
373If true, the process will not perform the typical double-fork, which is extra
374added protection from your process accidentally aquiring a controlling terminal.
375More information can be found by Googling "double fork daemonize".
376
377=item I<ignore_zombies Bool>
378
379If true, the process will not clean up zombie processes.
380Normally you don't want this.
381
382=item I<dont_close_all_files Bool>
383
384If true, the objects open filehandles will not be closed when daemonized.
385Normally you don't want this.
386
387
4327fe98 388=item I<is_daemon Bool>
389
5b9ebe08 390If true, the process is the backgrounded daemon process, if false it is the
391parent process. This is useful for example in an C<after 'start' => sub { }>
392block.
e7a196e7 393
4327fe98 394B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>.
b916501e 395
4327fe98 396=item I<stop_timeout>
b916501e 397
398Number of seconds to wait for the process to stop, before trying harder to kill
4327fe98 399it. Defaults to 2 seconds.
e7a196e7 400
a4952679 401=back
402
5b9ebe08 403These are the internal attributes, which are not available through MooseX::Getopt.
404
405=over 4
406
407=item I<exit_code Int>
408
92cf56b7 409=item I<status_message Str>
5b9ebe08 410
411=back
412
413=head1 METHODS
a4952679 414
4327fe98 415=head2 Daemon Control Methods
416
5b9ebe08 417These methods can be used to control the daemon behavior. Every effort
418has been made to have these methods DWIM (Do What I Mean), so that you
419can focus on just writing the code for your daemon.
a4952679 420
5b9ebe08 421Extending these methods is best done with the L<Moose> method modifiers,
4327fe98 422such as C<before>, C<after> and C<around>.
423
424=over 4
425
426=item B<start>
a4952679 427
428Setup a pidfile, fork, then setup the signal handlers.
429
4327fe98 430=item B<stop>
a4952679 431
432Stop the process matching the pidfile, and unlinks the pidfile.
433
4327fe98 434=item B<restart>
a4952679 435
4327fe98 436Literally this is:
a4952679 437
438 $self->stop();
439 $self->start();
440
5b9ebe08 441=item B<status>
442
92cf56b7 443=item B<shutdown>
444
4327fe98 445=back
446
5b9ebe08 447
4327fe98 448=head2 Pidfile Handling Methods
449
450=over 4
451
452=item B<init_pidfile>
453
454This method will create a L<MooseX::Daemonize::Pid::File> object and tell
455it to store the PID in the file C<$pidbase/$progname.pid>.
456
457=item B<check>
458
5b9ebe08 459This checks to see if the daemon process is currently running by checking
4327fe98 460the pidfile.
a4952679 461
4327fe98 462=item B<get_pid>
a4952679 463
4327fe98 464Returns the PID of the daemon process.
a4952679 465
4327fe98 466=item B<save_pid>
a4952679 467
4327fe98 468Write the pidfile.
469
470=item B<remove_pid>
471
472Removes the pidfile.
473
474=back
475
476=head2 Signal Handling Methods
477
478=over 4
479
480=item B<setup_signals>
481
5b9ebe08 482Setup the signal handlers, by default it only sets up handlers for SIGINT and
4327fe98 483SIGHUP. If you wish to add more signals just use the C<after> method modifier
484and add them.
485
486=item B<handle_sigint>
a4952679 487
cecbee2d 488Handle a INT signal, by default calls C<$self->stop()>
a4952679 489
4327fe98 490=item B<handle_sighup>
a4952679 491
cecbee2d 492Handle a HUP signal. By default calls C<$self->restart()>
a4952679 493
4327fe98 494=back
495
9c8a33b3 496=head2 Exit Code Methods
497
498These are overriable constant methods used for setting the exit code.
499
500=over 4
501
502=item OK
503
504Returns 0.
505
506=item ERROR
507
508Returns 1.
509
510=back
511
4327fe98 512=head2 Introspection
513
514=over 4
515
a4952679 516=item meta()
517
cecbee2d 518The C<meta()> method from L<Class::MOP::Class>
a4952679 519
520=back
521
522=head1 DEPENDENCIES
523
4327fe98 524L<Moose>, L<MooseX::Getopt>, L<MooseX::Types::Path::Class> and L<POSIX>
a4952679 525
526=head1 INCOMPATIBILITIES
527
4327fe98 528None reported. Although obviously this will not work on Windows.
a4952679 529
530=head1 BUGS AND LIMITATIONS
531
a4952679 532No bugs have been reported.
533
534Please report any bugs or feature requests to
535C<bug-acme-dahut-call@rt.cpan.org>, or through the web interface at
536L<http://rt.cpan.org>.
537
538=head1 SEE ALSO
539
4327fe98 540L<Proc::Daemon>, L<Daemon::Generic>
a4952679 541
92cf56b7 542=head1 AUTHORS
a4952679 543
69186a48 544Chris Prather C<< <chris@prather.org >>
a4952679 545
92cf56b7 546Stevan Little C<< <stevan.little@iinteractive.com> >>
547
7ada91b8 548=head1 THANKS
549
5b9ebe08 550Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the
b916501e 551#moose denzians
a4952679 552
637573c4 553Some bug fixes sponsored by Takkle Inc.
554
a4952679 555=head1 LICENCE AND COPYRIGHT
556
1d85c76d 557Copyright (c) 2007-2010, Chris Prather C<< <chris@prather.org> >>. Some rights
b916501e 558reserved.
a4952679 559
560This module is free software; you can redistribute it and/or
561modify it under the same terms as Perl itself. See L<perlartistic>.
562
a4952679 563=head1 DISCLAIMER OF WARRANTY
564
565BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
566FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
567OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
568PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
569EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
570WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
571ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
572YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
573NECESSARY SERVICING, REPAIR, OR CORRECTION.
574
575IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
576WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
577REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
578LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
579OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
580THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
581RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
582FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
583SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
584SUCH DAMAGES.
8ac4733f 585
586=cut