use strict; # because Kwalitee is pedantic
use Moose::Role;
use MooseX::Types::Path::Class;
+use File::Path qw(make_path);
-our $VERSION = 0.05;
+our $VERSION = '0.16';
with 'MooseX::Daemonize::WithPidFile',
'MooseX::Getopt';
-
-use constant OK => 0;
-use constant ERROR => 1;
+
+sub OK () { 0 }
+sub ERROR () { 1 }
has progname => (
metaclass => 'Getopt',
( my $name = lc $_[0]->meta->name ) =~ s/::/_/g;
return $name;
},
+ documentation => 'the name of the daemon',
);
has pidbase => (
coerce => 1,
required => 1,
lazy => 1,
- default => sub { Path::Class::Dir->new('var', 'run') },
+ default => sub { Path::Class::Dir->new('', 'var', 'run') },
+ documentation => 'the base for our pid (default: /var/run)',
);
has basedir => (
required => 1,
lazy => 1,
default => sub { Path::Class::Dir->new('/') },
+ documentation => 'the directory to chdir to (default: /)',
);
has foreground => (
isa => 'Bool',
is => 'ro',
default => sub { 0 },
+ documentation => 'if true, the process won\'t background',
);
has stop_timeout => (
metaclass => 'Getopt',
isa => 'Int',
is => 'rw',
- default => sub { 2 }
+ default => sub { 2 },
+ documentation => 'number of seconds to wait for the process to stop, before trying harder to kill it (default: 2 s)',
);
# internal book-keeping
sub init_pidfile {
my $self = shift;
my $file = $self->pidbase . '/' . $self->progname . '.pid';
+
+ if ( !-d $self->pidbase ) {
+ make_path( $self->pidbase, { error => \my $err } );
+ if (@$err) {
+ confess sprintf( "Cannot create pidbase directory '%s': %s",
+ $self->pidbase, @$err );
+ }
+ }
+
confess "Cannot write to $file" unless (-e $file ? -w $file : -w $self->pidbase);
MooseX::Daemonize::Pid::File->new( file => $file );
}
$self->clear_exit_code;
if ($self->pidfile->is_running) {
- $self->exit_code(OK);
- $self->status_message('Daemon is already running with pid (' . $self->pidfile->pid . ')');
+ $self->exit_code($self->OK);
+ $self->status_message('Daemon is already running with pid (' . $self->pidfile->pid . ')');
return !($self->exit_code);
}
-
- if ($self->foreground) {
+
+ if ($self->foreground) {
$self->is_daemon(1);
}
- else {
- eval { $self->daemonize };
+ else {
+ eval { $self->daemonize };
if ($@) {
- $self->exit_code(ERROR);
+ $self->exit_code($self->ERROR);
$self->status_message('Start failed : ' . $@);
return !($self->exit_code);
}
}
unless ($self->is_daemon) {
- $self->exit_code(OK);
+ $self->exit_code($self->OK);
$self->status_message('Start succeeded');
return !($self->exit_code);
}
$self->clear_exit_code;
if ($self->pidfile->is_running) {
- $self->exit_code(OK);
- $self->status_message('Daemon is running with pid (' . $self->pidfile->pid . ')');
+ $self->exit_code($self->OK);
+ $self->status_message('Daemon is running with pid (' . $self->pidfile->pid . ')');
}
- else {
- $self->exit_code(ERROR);
+ else {
+ $self->exit_code($self->ERROR);
$self->status_message('Daemon is not running with pid (' . $self->pidfile->pid . ')');
}
$self->clear_exit_code;
unless ($self->stop) {
- $self->exit_code(ERROR);
+ $self->exit_code($self->ERROR);
$self->status_message('Restart (Stop) failed : ' . $@);
}
unless ($self->start) {
- $self->exit_code(ERROR);
+ $self->exit_code($self->ERROR);
$self->status_message('Restart (Start) failed : ' . $@);
}
- if ($self->exit_code == OK) {
- $self->exit_code(OK);
+ if ($self->exit_code == $self->OK) {
+ $self->exit_code($self->OK);
$self->status_message("Restart successful");
}
eval { $self->$_kill($self->pidfile->pid) };
# and complain if we can't ...
if ($@) {
- $self->exit_code(ERROR);
+ $self->exit_code($self->ERROR);
$self->status_message('Stop failed : ' . $@);
}
# or gloat if we succeed ..
else {
- $self->exit_code(OK);
+ $self->exit_code($self->OK);
$self->status_message('Stop succeeded');
}
}
-
- # clean up ...
- eval { $self->pidfile->remove };
- if ($@) {
- warn "Could not remove pidfile ("
- . $self->pidfile->file
- . ") because : $!";
- }
-
}
else {
# this just returns the OK
# exit code for now, but
# we should make this overridable
- $self->exit_code(OK);
+ $self->exit_code($self->OK);
$self->status_message("Not running");
}
# $!{EPERM} could also be true if we cant kill it (permission error)
# Try SIGINT ... 2s ... SIGTERM ... 2s ... SIGKILL ... 3s ... UNDEAD!
+ my $terminating_signal;
for ( [ 2, $timeout ], [15, $timeout], [9, $timeout * 1.5] ) {
my ($signal, $timeout) = @$_;
$timeout = int $timeout;
CORE::kill($signal, $pid);
- last unless CORE::kill 0 => $pid or $!{EPERM};
-
while ($timeout) {
- sleep(1);
- last unless CORE::kill 0 => $pid or $!{EPERM};
+ unless(CORE::kill 0 => $pid or $!{EPERM}) {
+ $terminating_signal = $signal;
+ last;
+ }
$timeout--;
+ sleep(1) if $timeout;
}
+
+ last if $terminating_signal;
}
- return unless ( CORE::kill 0 => $pid or $!{EPERM} );
+ if($terminating_signal) {
+ if($terminating_signal == 9) {
+ # clean up the pidfile ourselves iff we used -9 and it worked
+ warn "Had to resort to 'kill -9' and it worked, wiping pidfile";
+ eval { $self->pidfile->remove };
+ if ($@) {
+ warn "Could not remove pidfile ("
+ . $self->pidfile->file
+ . ") because : $!";
+ }
+ }
+ return;
+ }
# IF it is still running
Carp::carp "$pid doesn't seem to want to die."; # AHH EVIL DEAD!
MooseX::Daemonize - Role for daemonizing your Moose based application
-=head1 VERSION
+=head1 WARNING
-This document describes MooseX::Daemonize version 0.05
+The maintainers of this module now recommend using L<Daemon::Control> instead.
=head1 SYNOPSIS
appropriately to Signals. This module provides a set of basic roles as an
infrastructure to do that.
+=head1 CAVEATS
+
+When going into background MooseX::Daemonize closes all open file
+handles. This may interfere with you logging because it may also close the log
+file handle you want to write to. To prevent this you can either defer opening
+the log file until after start. Alternatively, use can use the
+'dont_close_all_files' option either from the command line or in your .sh
+script.
+
+Assuming you want to use Log::Log4perl for example you could expand the
+MooseX::Daemonize example above like this.
+
+ after start => sub {
+ my $self = shift;
+ return unless $self->is_daemon;
+ Log::Log4perl->init(\$log4perl_config);
+ my $logger = Log::Log4perl->get_logger();
+ $logger->info("Daemon started");
+ # your daemon code here ...
+ };
+
+
=head1 ATTRIBUTES
This list includes attributes brought in from other roles as well
=item I<pidbase Path::Class::Dir | Str>
-The base for our bid, defaults to C</var/run/$progname>
+The base for our PID, defaults to C</var/run/>
+
+=item I<basedir Path::Class::Dir | Str>
+
+The directory we chdir to; defaults to C</>.
=item I<pidfile MooseX::Daemonize::Pid::File | Str>
-The file we store our PID in, defaults to C</var/run/$progname>
+The file we store our PID in, defaults to C<$pidbase/$progname.pid>
=item I<foreground Bool>
If true, the process won't background. Useful for debugging. This option can
be set via Getopt's -f.
+=item I<no_double_fork Bool>
+
+If true, the process will not perform the typical double-fork, which is extra
+added protection from your process accidentally aquiring a controlling terminal.
+More information can be found by Googling "double fork daemonize".
+
+=item I<ignore_zombies Bool>
+
+If true, the process will not clean up zombie processes.
+Normally you don't want this.
+
+=item I<dont_close_all_files Bool>
+
+If true, the objects open filehandles will not be closed when daemonized.
+Normally you don't want this.
+
+
=item I<is_daemon Bool>
If true, the process is the backgrounded daemon process, if false it is the
=back
+=head2 Exit Code Methods
+
+These are overriable constant methods used for setting the exit code.
+
+=over 4
+
+=item OK
+
+Returns 0.
+
+=item ERROR
+
+Returns 1.
+
+=back
+
=head2 Introspection
=over 4
=head1 SEE ALSO
-L<Proc::Daemon>, L<Daemon::Generic>
+L<Daemon::Control>, L<Proc::Daemon>, L<Daemon::Generic>
=head1 AUTHORS
-Chris Prather C<< <perigrin@cpan.org> >>
+Chris Prather C<< <chris@prather.org >>
Stevan Little C<< <stevan.little@iinteractive.com> >>
=head1 LICENCE AND COPYRIGHT
-Copyright (c) 2007, Chris Prather C<< <perigrin@cpan.org> >>. All rights
+Copyright (c) 2007-2011, Chris Prather C<< <chris@prather.org> >>. Some rights
reserved.
This module is free software; you can redistribute it and/or