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