fix version in pod
[gitmo/MooseX-Daemonize.git] / lib / MooseX / Daemonize.pm
index b16877d..4cb6cb3 100644 (file)
@@ -2,12 +2,16 @@ package MooseX::Daemonize;
 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';
 
+sub OK    () { 0 }
+sub ERROR () { 1 }
+
 has progname => (
     metaclass => 'Getopt',
     isa       => 'Str',
@@ -18,6 +22,7 @@ has progname => (
         ( my $name = lc $_[0]->meta->name ) =~ s/::/_/g;
         return $name;
     },
+    documentation => 'the name of the daemon',
 );
 
 has pidbase => (
@@ -27,7 +32,8 @@ 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 => (
@@ -38,6 +44,7 @@ has basedir => (
     required  => 1,
     lazy      => 1,
     default   => sub { Path::Class::Dir->new('/') },
+    documentation => 'the directory to chdir to (default: /)',
 );
 
 has foreground => (
@@ -46,13 +53,15 @@ 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
@@ -78,6 +87,15 @@ has exit_code => (
 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 );
 }
@@ -92,12 +110,16 @@ sub get_pid    { (shift)->pidfile->pid        }
 
 sub setup_signals {
     my $self = shift;
-    $SIG{'INT'} = sub { $self->handle_sigint };
-    $SIG{'HUP'} = sub { $self->handle_sighup };
+    $SIG{'INT'} = sub { $self->shutdown };
+# I can't think of a sane default here really ...
+#    $SIG{'HUP'} = sub { $self->handle_sighup };
 }
 
-sub handle_sigint { $_[0]->stop    }
-sub handle_sighup { $_[0]->restart }
+sub shutdown {
+    my $self = shift;
+    $self->pidfile->remove if $self->pidfile->pid == $$;
+    exit(0);
+}
 
 ## daemon control methods ...
 
@@ -108,23 +130,25 @@ sub start {
     $self->clear_exit_code;
 
     if ($self->pidfile->is_running) {
+        $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(1);
+            $self->exit_code($self->ERROR);
             $self->status_message('Start failed : ' . $@);
             return !($self->exit_code);
         }
     }
 
     unless ($self->is_daemon) {
+        $self->exit_code($self->OK);
         $self->status_message('Start succeeded');
         return !($self->exit_code);
     }
@@ -146,10 +170,11 @@ sub status {
     $self->clear_exit_code;
 
     if ($self->pidfile->is_running) {
-        $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(1);
+    else {
+        $self->exit_code($self->ERROR);
         $self->status_message('Daemon is not running with pid (' . $self->pidfile->pid . ')');
     }
 
@@ -163,17 +188,19 @@ sub restart {
     $self->clear_exit_code;
 
     unless ($self->stop) {
-        $self->exit_code(1);
+        $self->exit_code($self->ERROR);
         $self->status_message('Restart (Stop) failed : ' . $@);
     }
 
     unless ($self->start) {
-        $self->exit_code(1);
+        $self->exit_code($self->ERROR);
         $self->status_message('Restart (Start) failed : ' . $@);
     }
 
-    $self->status_message("Restart successful")
-        if !$self->exit_code;
+    if ($self->exit_code == $self->OK) {
+        $self->exit_code($self->OK);
+        $self->status_message("Restart successful");
+    }
 
     return !($self->exit_code);
 }
@@ -201,29 +228,22 @@ sub stop {
             eval { $self->$_kill($self->pidfile->pid) };
             # and complain if we can't ...
             if ($@) {
-                $self->exit_code(1);
+                $self->exit_code($self->ERROR);
                 $self->status_message('Stop failed : ' . $@);
             }
             # or gloat if we succeed ..
             else {
+                $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($self->OK);
         $self->status_message("Not running");
     }
 
@@ -237,15 +257,12 @@ $_kill = sub {
     my ( $self, $pid ) = @_;
     return unless $pid;
     unless ( CORE::kill 0 => $pid ) {
-
         # warn "$pid already appears dead.";
         return;
     }
 
     if ( $pid eq $$ ) {
-
-        # warn "$pid is us! Can't commit suicide.";
-        return;
+        die "$pid is us! Can't commit suicide.";
     }
 
     my $timeout = $self->stop_timeout;
@@ -253,32 +270,39 @@ $_kill = sub {
     # kill 0 => $pid returns 0 if the process is dead
     # $!{EPERM} could also be true if we cant kill it (permission error)
 
-    # if this is being called
-    # inside the daemon then
-    # we don't want sig-INT to
-    # fall into a loop here
-    # so we reset it.
-    if ($self->is_daemon) {
-        $SIG{INT} = 'DEFAULT';
-    }
-
     # 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!
@@ -291,12 +315,11 @@ __END__
 
 =head1 NAME
 
-MooseX::Daemonize - provides a Role that daemonizes your Moose based
-application.
+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
 
@@ -325,7 +348,7 @@ This document describes MooseX::Daemonize version 0.05
     $daemon->restart if $command eq 'restart';
     $daemon->stop    if $command eq 'stop';
 
-    warn($daemon->status);
+    warn($daemon->status_message);
     exit($daemon->exit_code);
 
 =head1 DESCRIPTION
@@ -334,6 +357,28 @@ Often you want to write a persistant daemon that has a pid file, and responds
 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
@@ -349,17 +394,38 @@ The name of our daemon, defaults to C<$package_name =~ s/::/_/>;
 
 =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
@@ -381,7 +447,7 @@ These are the internal attributes, which are not available through MooseX::Getop
 
 =item I<exit_code Int>
 
-=item I<status Str>
+=item I<status_message Str>
 
 =back
 
@@ -415,6 +481,8 @@ Literally this is:
 
 =item B<status>
 
+=item B<shutdown>
+
 =back
 
 
@@ -466,6 +534,22 @@ Handle a HUP signal. By default calls C<$self->restart()>
 
 =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
@@ -494,11 +578,13 @@ L<http://rt.cpan.org>.
 
 =head1 SEE ALSO
 
-L<Proc::Daemon>, L<Daemon::Generic>
+L<Daemon::Control>, L<Proc::Daemon>, L<Daemon::Generic>
+
+=head1 AUTHORS
 
-=head1 AUTHOR
+Chris Prather  C<< <chris@prather.org >>
 
-Chris Prather  C<< <perigrin@cpan.org> >>
+Stevan Little  C<< <stevan.little@iinteractive.com> >>
 
 =head1 THANKS
 
@@ -509,7 +595,7 @@ Some bug fixes sponsored by Takkle Inc.
 
 =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