cleaning up stuff
[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';
92cf56b7 10
11use constant OK => 0;
12use constant 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,
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) {
92cf56b7 118 $self->exit_code(OK);
119 $self->status_message('Daemon is already running with pid (' . $self->pidfile->pid . ')');
5b9ebe08 120 return !($self->exit_code);
121 }
8ac4733f 122
5b9ebe08 123 if ($self->foreground) {
124 $self->is_daemon(1);
125 }
126 else {
127 eval { $self->daemonize };
128 if ($@) {
92cf56b7 129 $self->exit_code(ERROR);
5b9ebe08 130 $self->status_message('Start failed : ' . $@);
131 return !($self->exit_code);
132 }
133 }
cbff8e52 134
5b9ebe08 135 unless ($self->is_daemon) {
92cf56b7 136 $self->exit_code(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) {
92cf56b7 158 $self->exit_code(OK);
5b9ebe08 159 $self->status_message('Daemon is running with pid (' . $self->pidfile->pid . ')');
160 }
161 else {
92cf56b7 162 $self->exit_code(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) {
92cf56b7 176 $self->exit_code(ERROR);
5b9ebe08 177 $self->status_message('Restart (Stop) failed : ' . $@);
178 }
179
180 unless ($self->start) {
92cf56b7 181 $self->exit_code(ERROR);
5b9ebe08 182 $self->status_message('Restart (Start) failed : ' . $@);
183 }
184
92cf56b7 185 if ($self->exit_code == OK) {
186 $self->exit_code(OK);
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 ($@) {
92cf56b7 216 $self->exit_code(ERROR);
5b9ebe08 217 $self->status_message('Stop failed : ' . $@);
218 }
219 # or gloat if we succeed ..
220 else {
92cf56b7 221 $self->exit_code(OK);
5b9ebe08 222 $self->status_message('Stop succeeded');
223 }
224
225 }
226
227 # clean up ...
228 eval { $self->pidfile->remove };
229 if ($@) {
230 warn "Could not remove pidfile ("
231 . $self->pidfile->file
232 . ") because : $!";
233 }
234
235 }
236 else {
237 # this just returns the OK
238 # exit code for now, but
239 # we should make this overridable
92cf56b7 240 $self->exit_code(OK);
5b9ebe08 241 $self->status_message("Not running");
242 }
243
244 # if we are returning to our script
245 # then we actually need the opposite
246 # of what the system/OS expects
247 return !($self->exit_code);
3543c999 248}
249
b916501e 250$_kill = sub {
a4952679 251 my ( $self, $pid ) = @_;
2361a590 252 return unless $pid;
3543c999 253 unless ( CORE::kill 0 => $pid ) {
3543c999 254 # warn "$pid already appears dead.";
255 return;
256 }
257
258 if ( $pid eq $$ ) {
4a24225a 259 die "$pid is us! Can't commit suicide.";
a4952679 260 }
261
b916501e 262 my $timeout = $self->stop_timeout;
a4952679 263
b916501e 264 # kill 0 => $pid returns 0 if the process is dead
265 # $!{EPERM} could also be true if we cant kill it (permission error)
a4952679 266
b916501e 267 # Try SIGINT ... 2s ... SIGTERM ... 2s ... SIGKILL ... 3s ... UNDEAD!
268 for ( [ 2, $timeout ], [15, $timeout], [9, $timeout * 1.5] ) {
ea9485d8 269 my ($signal, $timeout) = @$_;
270 $timeout = int $timeout;
5b9ebe08 271
ea9485d8 272 CORE::kill($signal, $pid);
5b9ebe08 273
b916501e 274 last unless CORE::kill 0 => $pid or $!{EPERM};
5b9ebe08 275
ea9485d8 276 while ($timeout) {
277 sleep(1);
278 last unless CORE::kill 0 => $pid or $!{EPERM};
279 $timeout--;
280 }
a4952679 281 }
282
b916501e 283 return unless ( CORE::kill 0 => $pid or $!{EPERM} );
284
285 # IF it is still running
d9e417f4 286 Carp::carp "$pid doesn't seem to want to die."; # AHH EVIL DEAD!
b916501e 287};
a4952679 288
2891;
290__END__
291
8ac4733f 292=pod
293
a4952679 294=head1 NAME
295
5b9ebe08 296MooseX::Daemonize - provides a Role that daemonizes your Moose based
b916501e 297application.
a4952679 298
299=head1 VERSION
300
4327fe98 301This document describes MooseX::Daemonize version 0.05
a4952679 302
303=head1 SYNOPSIS
304
4327fe98 305 package My::Daemon;
a4952679 306 use Moose;
5b9ebe08 307
a4952679 308 with qw(MooseX::Daemonize);
5b9ebe08 309
4327fe98 310 # ... define your class ....
5b9ebe08 311
312 after start => sub {
4327fe98 313 my $self = shift;
314 return unless $self->is_daemon;
315 # your daemon code here ...
316 };
a4952679 317
5b9ebe08 318 # then in your script ...
319
4327fe98 320 my $daemon = My::Daemon->new_with_options();
5b9ebe08 321
4327fe98 322 my ($command) = @{$daemon->extra_argv}
323 defined $command || die "No command specified";
5b9ebe08 324
325 $daemon->start if $command eq 'start';
326 $daemon->status if $command eq 'status';
327 $daemon->restart if $command eq 'restart';
328 $daemon->stop if $command eq 'stop';
329
92cf56b7 330 warn($daemon->status_message);
5b9ebe08 331 exit($daemon->exit_code);
332
a4952679 333=head1 DESCRIPTION
334
b916501e 335Often you want to write a persistant daemon that has a pid file, and responds
5b9ebe08 336appropriately to Signals. This module provides a set of basic roles as an
4327fe98 337infrastructure to do that.
a4952679 338
339=head1 ATTRIBUTES
340
4327fe98 341This list includes attributes brought in from other roles as well
342we include them here for ease of documentation. All of these attributes
5b9ebe08 343are settable though L<MooseX::Getopt>'s command line handling, with the
4327fe98 344exception of C<is_daemon>.
345
a4952679 346=over
347
4327fe98 348=item I<progname Path::Class::Dir | Str>
a4952679 349
4327fe98 350The name of our daemon, defaults to C<$package_name =~ s/::/_/>;
a4952679 351
4327fe98 352=item I<pidbase Path::Class::Dir | Str>
a4952679 353
4327fe98 354The base for our bid, defaults to C</var/run/$progname>
a4952679 355
4327fe98 356=item I<pidfile MooseX::Daemonize::Pid::File | Str>
a4952679 357
4327fe98 358The file we store our PID in, defaults to C</var/run/$progname>
a4952679 359
4327fe98 360=item I<foreground Bool>
a4952679 361
5b9ebe08 362If true, the process won't background. Useful for debugging. This option can
b916501e 363be set via Getopt's -f.
a4952679 364
4327fe98 365=item I<is_daemon Bool>
366
5b9ebe08 367If true, the process is the backgrounded daemon process, if false it is the
368parent process. This is useful for example in an C<after 'start' => sub { }>
369block.
e7a196e7 370
4327fe98 371B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>.
b916501e 372
4327fe98 373=item I<stop_timeout>
b916501e 374
375Number of seconds to wait for the process to stop, before trying harder to kill
4327fe98 376it. Defaults to 2 seconds.
e7a196e7 377
a4952679 378=back
379
5b9ebe08 380These are the internal attributes, which are not available through MooseX::Getopt.
381
382=over 4
383
384=item I<exit_code Int>
385
92cf56b7 386=item I<status_message Str>
5b9ebe08 387
388=back
389
390=head1 METHODS
a4952679 391
4327fe98 392=head2 Daemon Control Methods
393
5b9ebe08 394These methods can be used to control the daemon behavior. Every effort
395has been made to have these methods DWIM (Do What I Mean), so that you
396can focus on just writing the code for your daemon.
a4952679 397
5b9ebe08 398Extending these methods is best done with the L<Moose> method modifiers,
4327fe98 399such as C<before>, C<after> and C<around>.
400
401=over 4
402
403=item B<start>
a4952679 404
405Setup a pidfile, fork, then setup the signal handlers.
406
4327fe98 407=item B<stop>
a4952679 408
409Stop the process matching the pidfile, and unlinks the pidfile.
410
4327fe98 411=item B<restart>
a4952679 412
4327fe98 413Literally this is:
a4952679 414
415 $self->stop();
416 $self->start();
417
5b9ebe08 418=item B<status>
419
92cf56b7 420=item B<shutdown>
421
4327fe98 422=back
423
5b9ebe08 424
4327fe98 425=head2 Pidfile Handling Methods
426
427=over 4
428
429=item B<init_pidfile>
430
431This method will create a L<MooseX::Daemonize::Pid::File> object and tell
432it to store the PID in the file C<$pidbase/$progname.pid>.
433
434=item B<check>
435
5b9ebe08 436This checks to see if the daemon process is currently running by checking
4327fe98 437the pidfile.
a4952679 438
4327fe98 439=item B<get_pid>
a4952679 440
4327fe98 441Returns the PID of the daemon process.
a4952679 442
4327fe98 443=item B<save_pid>
a4952679 444
4327fe98 445Write the pidfile.
446
447=item B<remove_pid>
448
449Removes the pidfile.
450
451=back
452
453=head2 Signal Handling Methods
454
455=over 4
456
457=item B<setup_signals>
458
5b9ebe08 459Setup the signal handlers, by default it only sets up handlers for SIGINT and
4327fe98 460SIGHUP. If you wish to add more signals just use the C<after> method modifier
461and add them.
462
463=item B<handle_sigint>
a4952679 464
cecbee2d 465Handle a INT signal, by default calls C<$self->stop()>
a4952679 466
4327fe98 467=item B<handle_sighup>
a4952679 468
cecbee2d 469Handle a HUP signal. By default calls C<$self->restart()>
a4952679 470
4327fe98 471=back
472
473=head2 Introspection
474
475=over 4
476
a4952679 477=item meta()
478
cecbee2d 479The C<meta()> method from L<Class::MOP::Class>
a4952679 480
481=back
482
483=head1 DEPENDENCIES
484
4327fe98 485L<Moose>, L<MooseX::Getopt>, L<MooseX::Types::Path::Class> and L<POSIX>
a4952679 486
487=head1 INCOMPATIBILITIES
488
4327fe98 489None reported. Although obviously this will not work on Windows.
a4952679 490
491=head1 BUGS AND LIMITATIONS
492
a4952679 493No bugs have been reported.
494
495Please report any bugs or feature requests to
496C<bug-acme-dahut-call@rt.cpan.org>, or through the web interface at
497L<http://rt.cpan.org>.
498
499=head1 SEE ALSO
500
4327fe98 501L<Proc::Daemon>, L<Daemon::Generic>
a4952679 502
92cf56b7 503=head1 AUTHORS
a4952679 504
505Chris Prather C<< <perigrin@cpan.org> >>
506
92cf56b7 507Stevan Little C<< <stevan.little@iinteractive.com> >>
508
7ada91b8 509=head1 THANKS
510
5b9ebe08 511Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the
b916501e 512#moose denzians
a4952679 513
637573c4 514Some bug fixes sponsored by Takkle Inc.
515
a4952679 516=head1 LICENCE AND COPYRIGHT
517
5b9ebe08 518Copyright (c) 2007, Chris Prather C<< <perigrin@cpan.org> >>. All rights
b916501e 519reserved.
a4952679 520
521This module is free software; you can redistribute it and/or
522modify it under the same terms as Perl itself. See L<perlartistic>.
523
a4952679 524=head1 DISCLAIMER OF WARRANTY
525
526BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
527FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
528OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
529PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
530EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
531WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
532ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
533YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
534NECESSARY SERVICING, REPAIR, OR CORRECTION.
535
536IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
537WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
538REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
539LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
540OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
541THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
542RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
543FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
544SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
545SUCH DAMAGES.
8ac4733f 546
547=cut