From: Stevan Little Date: Wed, 19 Dec 2007 02:48:39 +0000 (+0000) Subject: bunch of stuff X-Git-Tag: 0_06~5 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5b9ebe08eff2f6410804c2f3c2fa361db0b446a2;p=gitmo%2FMooseX-Daemonize.git bunch of stuff --- diff --git a/IDEAS b/IDEAS index 9e1eab8..992910c 100644 --- 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] I guess what I'm saying is that start() has three possible results: success, already running, failed to start +[12:17] I am not as familair as you are with this stuff so I am not 100% of al the things +[12:17] and stop() does too: success, not running, failed to stop +[12:17] k +[12:17] perfect +[12:17] I will add em :) +... +[12:18] can you map the exit value to those three states (sorry I am not familiar with these details myself) +[12:18] for most people, the right answer is that start()'s success/already_running should return OK to the OS, failed returns error +[12:18] OK = 0, ERROR = 1? +[12:18] and stop()'s success/not_running should also be OK (exit 0) \ No newline at end of file diff --git a/lib/MooseX/Daemonize.pm b/lib/MooseX/Daemonize.pm index c67811a..b16877d 100644 --- a/lib/MooseX/Daemonize.pm +++ b/lib/MooseX/Daemonize.pm @@ -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's command line handling, with the +are settable though L's command line handling, with the exception of C. =over @@ -232,14 +357,14 @@ The file we store our PID in, defaults to C =item I -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 -If true, the process is the backgrounded daemon process, if false it is the -parent process. This is useful for example in an C 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 sub { }> +block. B This option is explicitly B available through L. @@ -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 + +=item I + +=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 method modifiers, +Extending these methods is best done with the L method modifiers, such as C, C and C. =over 4 @@ -278,8 +413,11 @@ Literally this is: $self->stop(); $self->start(); +=item B + =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 -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 @@ -314,7 +452,7 @@ Removes the pidfile. =item B -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 method modifier and add them. @@ -364,14 +502,14 @@ Chris Prather C<< >> =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<< >>. All rights +Copyright (c) 2007, Chris Prather C<< >>. All rights reserved. This module is free software; you can redistribute it and/or diff --git a/lib/MooseX/Daemonize/Pid.pm b/lib/MooseX/Daemonize/Pid.pm index 7df8070..bcfd3b5 100644 --- a/lib/MooseX/Daemonize/Pid.pm +++ b/lib/MooseX/Daemonize/Pid.pm @@ -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 => $_ ) }; diff --git a/lib/MooseX/Daemonize/WithPidFile.pm b/lib/MooseX/Daemonize/WithPidFile.pm index 89741ba..65df993 100644 --- a/lib/MooseX/Daemonize/WithPidFile.pm +++ b/lib/MooseX/Daemonize/WithPidFile.pm @@ -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; diff --git a/t/01.filecreate.t b/t/01.filecreate.t index 01cc2b7..8f108f8 100644 --- a/t/01.filecreate.t +++ b/t/01.filecreate.t @@ -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};