bunch of stuff
Stevan Little [Wed, 19 Dec 2007 02:48:39 +0000 (02:48 +0000)]
IDEAS
lib/MooseX/Daemonize.pm
lib/MooseX/Daemonize/Pid.pm
lib/MooseX/Daemonize/WithPidFile.pm
t/01.filecreate.t

diff --git a/IDEAS b/IDEAS
index 9e1eab8..992910c 100644 (file)
--- a/IDEAS
+++ b/IDEAS
@@ -1 +1,16 @@
-* 2:36 : mike : A slightly different would be to have another object, which is a daemon, which is the keeper of the PIDs. It can bind to a port and then your other objects would actually cause things to happen without needing a PID file.
+* 2:36 : mike : A slightly different would be to have another object, which is a 
+daemon, which is the keeper of the PIDs. It can bind to a port and then your other 
+objects would actually cause things to happen without needing a PID file.
+
+
+[12:16]  <blblack> I guess what I'm saying is that start() has three possible results: success, already running, failed to start
+[12:17]  <stevan> I am not as familair as you are with this stuff so I am not 100% of al the things
+[12:17]  <blblack> and stop() does too: success, not running, failed to stop
+[12:17]  <stevan> k
+[12:17]  <stevan> perfect
+[12:17]  <stevan> I will add em :)
+...
+[12:18]  <stevan> can you map the exit value to those three states (sorry I am not familiar with these details myself)
+[12:18]  <blblack> for most people, the right answer is that start()'s success/already_running should return OK to the OS, failed returns error
+[12:18]  <stevan> OK = 0, ERROR = 1?
+[12:18]  <blblack> and stop()'s success/not_running should also be OK (exit 0)
\ No newline at end of file
index c67811a..b16877d 100644 (file)
@@ -9,7 +9,7 @@ with 'MooseX::Daemonize::WithPidFile',
      'MooseX::Getopt';
 
 has progname => (
-    metaclass => 'Getopt',    
+    metaclass => 'Getopt',
     isa       => 'Str',
     is        => 'ro',
     lazy      => 1,
@@ -21,17 +21,17 @@ has progname => (
 );
 
 has pidbase => (
-    metaclass => 'Getopt',    
+    metaclass => 'Getopt',
     isa       => 'Path::Class::Dir',
     is        => 'ro',
     coerce    => 1,
-    required  => 1,    
+    required  => 1,
     lazy      => 1,
     default   => sub { Path::Class::Dir->new('var', 'run') },
 );
 
 has basedir => (
-    metaclass => 'Getopt',    
+    metaclass => 'Getopt',
     isa       => 'Path::Class::Dir',
     is        => 'ro',
     coerce    => 1,
@@ -49,12 +49,28 @@ has foreground => (
 );
 
 has stop_timeout => (
-    metaclass => 'Getopt',    
+    metaclass => 'Getopt',
     isa       => 'Int',
     is        => 'rw',
     default   => sub { 2 }
 );
 
+# internal book-keeping
+
+has status_message => (
+    metaclass => 'NoGetopt',
+    isa       => 'Str',
+    is        => 'rw',
+    clearer   => 'clear_status_message',
+);
+
+has exit_code => (
+    metaclass => 'NoGetopt',
+    isa       => 'Int',
+    is        => 'rw',
+    clearer   => 'clear_exit_code',
+);
+
 # methods ...
 
 ## PID file related stuff ...
@@ -66,7 +82,7 @@ sub init_pidfile {
     MooseX::Daemonize::Pid::File->new( file => $file );
 }
 
-# backwards compat, 
+# backwards compat,
 sub check      { (shift)->pidfile->is_running }
 sub save_pid   { (shift)->pidfile->write      }
 sub remove_pid { (shift)->pidfile->remove     }
@@ -77,24 +93,43 @@ 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{'HUP'} = sub { $self->handle_sighup };
 }
 
-sub handle_sigint { $_[0]->stop; }
-sub handle_sighup { $_[0]->restart; }
+sub handle_sigint { $_[0]->stop    }
+sub handle_sighup { $_[0]->restart }
 
 ## daemon control methods ...
 
 sub start {
-    my ($self) = @_;
-    
-    confess "instance already running" if $self->pidfile->is_running;
-    
-    $self->daemonize unless $self->foreground;
+    my $self = shift;
+
+    $self->clear_status_message;
+    $self->clear_exit_code;
+
+    if ($self->pidfile->is_running) {
+        $self->status_message('Daemon is already running with pid (' . $self->pidfile->pid . ')');
+        return !($self->exit_code);
+    }
     
-    return unless $self->is_daemon;
+    if ($self->foreground) { 
+        $self->is_daemon(1);
+    }
+    else {      
+        eval { $self->daemonize };              
+        if ($@) {
+            $self->exit_code(1);
+            $self->status_message('Start failed : ' . $@);
+            return !($self->exit_code);
+        }
+    }
 
-    $self->pidfile->pid($$);   
+    unless ($self->is_daemon) {
+        $self->status_message('Start succeeded');
+        return !($self->exit_code);
+    }
+
+    $self->pidfile->pid($$);
 
     # Change to basedir
     chdir $self->basedir;
@@ -104,22 +139,98 @@ sub start {
     return $$;
 }
 
+sub status {
+    my $self = shift;
+
+    $self->clear_status_message;
+    $self->clear_exit_code;
+
+    if ($self->pidfile->is_running) {
+        $self->status_message('Daemon is running with pid (' . $self->pidfile->pid . ')');    
+    }
+    else {            
+        $self->exit_code(1);
+        $self->status_message('Daemon is not running with pid (' . $self->pidfile->pid . ')');
+    }
+
+    return !($self->exit_code);
+}
+
 sub restart {
-    my ($self) = @_;
-    $self->stop( no_exit => 1 );
-    $self->start();
+    my $self = shift;
+
+    $self->clear_status_message;
+    $self->clear_exit_code;
+
+    unless ($self->stop) {
+        $self->exit_code(1);
+        $self->status_message('Restart (Stop) failed : ' . $@);
+    }
+
+    unless ($self->start) {
+        $self->exit_code(1);
+        $self->status_message('Restart (Start) failed : ' . $@);
+    }
+
+    $self->status_message("Restart successful")
+        if !$self->exit_code;
+
+    return !($self->exit_code);
 }
 
 # Make _kill *really* private
 my $_kill;
 
 sub stop {
-    my ( $self, %args ) = @_;
-    my $pid = $self->pidfile->pid;
-    $self->$_kill($pid) unless $self->foreground();
-    $self->pidfile->remove;
-    return 1 if $args{no_exit};
-    exit;
+    my $self = shift;
+
+    $self->clear_status_message;
+    $self->clear_exit_code;
+
+    # if the pid is not running
+    # then we dont need to stop
+    # anything ...
+    if ($self->pidfile->is_running) {
+
+        # if we are foreground, then
+        # no need to try and kill
+        # ourselves
+        unless ($self->foreground) {
+
+            # kill the process ...
+            eval { $self->$_kill($self->pidfile->pid) };
+            # and complain if we can't ...
+            if ($@) {
+                $self->exit_code(1);
+                $self->status_message('Stop failed : ' . $@);
+            }
+            # or gloat if we succeed ..
+            else {
+                $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->status_message("Not running");
+    }
+
+    # if we are returning to our script
+    # then we actually need the opposite
+    # of what the system/OS expects
+    return !($self->exit_code);
 }
 
 $_kill = sub {
@@ -142,15 +253,24 @@ $_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!
     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};
@@ -171,7 +291,7 @@ __END__
 
 =head1 NAME
 
-MooseX::Daemonize - provides a Role that daemonizes your Moose based 
+MooseX::Daemonize - provides a Role that daemonizes your Moose based
 application.
 
 =head1 VERSION
@@ -182,38 +302,43 @@ This document describes MooseX::Daemonize version 0.05
 
     package My::Daemon;
     use Moose;
-    
+
     with qw(MooseX::Daemonize);
-    
+
     # ... define your class ....
-    
-    after start => sub { 
+
+    after start => sub {
         my $self = shift;
         return unless $self->is_daemon;
         # your daemon code here ...
     };
 
-    # then in your script ... 
-    
+    # then in your script ...
+
     my $daemon = My::Daemon->new_with_options();
-    
+
     my ($command) = @{$daemon->extra_argv}
     defined $command || die "No command specified";
-    
-    $daemon->start() if $command eq 'start';
-    $daemon->stop()  if $command eq 'stop';
-     
+
+    $daemon->start   if $command eq 'start';
+    $daemon->status  if $command eq 'status';
+    $daemon->restart if $command eq 'restart';
+    $daemon->stop    if $command eq 'stop';
+
+    warn($daemon->status);
+    exit($daemon->exit_code);
+
 =head1 DESCRIPTION
 
 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  
+appropriately to Signals. This module provides a set of basic roles as an
 infrastructure to do that.
 
 =head1 ATTRIBUTES
 
 This list includes attributes brought in from other roles as well
 we include them here for ease of documentation. All of these attributes
-are settable though L<MooseX::Getopt>'s command line handling, with the 
+are settable though L<MooseX::Getopt>'s command line handling, with the
 exception of C<is_daemon>.
 
 =over
@@ -232,14 +357,14 @@ The file we store our PID in, defaults to C</var/run/$progname>
 
 =item I<foreground Bool>
 
-If true, the process won't background. Useful for debugging. This option can 
+If true, the process won't background. Useful for debugging. This option can
 be set via Getopt's -f.
 
 =item I<is_daemon Bool>
 
-If true, the process is the backgrounded daemon process, if false it is the 
-parent process. This is useful for example in an C<after 'start' => sub { }> 
-block. 
+If true, the process is the backgrounded daemon process, if false it is the
+parent process. This is useful for example in an C<after 'start' => sub { }>
+block.
 
 B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>.
 
@@ -250,15 +375,25 @@ it. Defaults to 2 seconds.
 
 =back
 
-=head1 METHODS 
+These are the internal attributes, which are not available through MooseX::Getopt.
+
+=over 4
+
+=item I<exit_code Int>
+
+=item I<status Str>
+
+=back
+
+=head1 METHODS
 
 =head2 Daemon Control Methods
 
-These methods can be used to control the daemon behavior. Every effort 
-has been made to have these methods DWIM (Do What I Mean), so that you 
-can focus on just writing the code for your daemon. 
+These methods can be used to control the daemon behavior. Every effort
+has been made to have these methods DWIM (Do What I Mean), so that you
+can focus on just writing the code for your daemon.
 
-Extending these methods is best done with the L<Moose> method modifiers, 
+Extending these methods is best done with the L<Moose> method modifiers,
 such as C<before>, C<after> and C<around>.
 
 =over 4
@@ -278,8 +413,11 @@ Literally this is:
     $self->stop();
     $self->start();
 
+=item B<status>
+
 =back
 
+
 =head2 Pidfile Handling Methods
 
 =over 4
@@ -291,7 +429,7 @@ it to store the PID in the file C<$pidbase/$progname.pid>.
 
 =item B<check>
 
-This checks to see if the daemon process is currently running by checking 
+This checks to see if the daemon process is currently running by checking
 the pidfile.
 
 =item B<get_pid>
@@ -314,7 +452,7 @@ Removes the pidfile.
 
 =item B<setup_signals>
 
-Setup the signal handlers, by default it only sets up handlers for SIGINT and 
+Setup the signal handlers, by default it only sets up handlers for SIGINT and
 SIGHUP. If you wish to add more signals just use the C<after> method modifier
 and add them.
 
@@ -364,14 +502,14 @@ Chris Prather  C<< <perigrin@cpan.org> >>
 
 =head1 THANKS
 
-Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the 
+Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the
 #moose denzians
 
 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, Chris Prather C<< <perigrin@cpan.org> >>. All rights
 reserved.
 
 This module is free software; you can redistribute it and/or
index 7df8070..bcfd3b5 100644 (file)
@@ -3,10 +3,6 @@ use strict;    # because Kwalitee is pedantic
 use Moose;
 use Moose::Util::TypeConstraints;
 
-use overload '""'     => sub { (shift)->pid },
-             '+0'     => sub { (shift)->pid },
-             fallback => 1;
-
 coerce 'MooseX::Daemonize::Pid' 
     => from 'Int' 
         => via { MooseX::Daemonize::Pid->new( pid => $_ ) };
index 89741ba..65df993 100644 (file)
@@ -20,7 +20,6 @@ has pidfile => (
     isa       => 'MooseX::Daemonize::Pid::File',
     is        => 'rw',
     lazy      => 1,
-    required  => 1,
     coerce    => 1,
     predicate => 'has_pidfile',
     builder   => 'init_pidfile',
@@ -33,7 +32,9 @@ after 'daemonize' => sub {
     # any bad PID values stashed around
     # - SL
     $self->pidfile->clear_pid;
-    $self->pidfile->write if $self->is_daemon;
+    if ($self->is_daemon) {
+        $self->pidfile->write;
+    }
 };
 
 1;
index 01cc2b7..8f108f8 100644 (file)
@@ -1,10 +1,19 @@
-use Test::More tests => 4;
-use Test::MooseX::Daemonize;
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Cwd;
+use File::Spec::Functions;
+
+use Test::More tests => 9;
 use MooseX::Daemonize;
 
-##  Since a daemon will not be able to print terminal output, we
-##  have a test daemon create a file, and another process test for
-##  its existence.
+use constant DEBUG => 0;
+
+my $CWD                = Cwd::cwd;
+my $FILENAME           = "$CWD/im_alive";
+$ENV{MX_DAEMON_STDOUT} = catfile($CWD, 'Out.txt');
+$ENV{MX_DAEMON_STDERR} = catfile($CWD, 'Err.txt');
 
 {
 
@@ -16,8 +25,9 @@ use MooseX::Daemonize;
     
     after start => sub { 
         my $self = shift;
-        $self->create_file( $self->filename ) 
-            if $self->is_daemon;
+        if ($self->is_daemon) {
+            $self->create_file( $self->filename );
+        }
     };
 
     sub create_file {
@@ -25,25 +35,48 @@ use MooseX::Daemonize;
         open( my $FILE, ">$file" ) || die $!;
         close($FILE);
     }
-
-    no Moose;
 }
 
-package main;
-use strict;
-use warnings;
-use Cwd;
-
-## Try to make sure we are in the test directory
-chdir 't' if ( Cwd::cwd() !~ m|/t$| );
-my $cwd = Cwd::cwd();
-
 my $app = FileMaker->new(
-    pidbase  => $cwd,
-    filename => "$cwd/im_alive",
+    pidbase  => $CWD,
+    filename => $FILENAME,
 );
-daemonize_ok( $app, 'child forked okay' );
+
+ok(!$app->status, '... the daemon is running');
+
+diag $$ if DEBUG;
+
+ok($app->start, '... daemon started');
+sleep(1); # give it a second ...
+
+ok($app->status, '... the daemon is running');
+
+my $pid = $app->pidfile->pid;
+isnt($pid, $$, '... the pid in our pidfile is correct (and not us)');
+
+if (DEBUG) {
+    diag `ps $pid`;
+    diag "Status is: " . $app->status_message;    
+}
+
 ok( -e $app->filename, "file exists" );
-ok( $app->stop( no_exit => 1 ), 'app stopped' );
-ok( not(-e $app->pidfile) , 'pidfile gone' );
-unlink( $app->filename );
+ok($app->status, '... the daemon is still running');
+
+if (DEBUG) {
+    diag `ps $pid`;
+    diag "Status is: " . $app->status_message;    
+}
+
+ok( $app->stop, 'app stopped' );
+ok(!$app->status, '... the daemon is no longer running');
+
+if (DEBUG) {
+    diag `ps $pid`;
+    diag "Status is: " . $app->status_message;    
+}
+
+ok( not(-e $app->pidfile->file) , 'pidfile gone' );
+
+unlink $FILENAME;
+unlink $ENV{MX_DAEMON_STDOUT};
+unlink $ENV{MX_DAEMON_STDERR};