X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMooseX%2FDaemonize.pm;h=4cb6cb374697df86267a3b446d3390f099b5d4bc;hb=25a727cc1b7fc1dcbf22894fefb23ea88ec2799d;hp=4ce6fd33ad3bc1ec10ea47a42b6dbd81b0a5a4ac;hpb=4a24225a4a5ba994ccec944e4bc617f0506d2eed;p=gitmo%2FMooseX-Daemonize.git diff --git a/lib/MooseX/Daemonize.pm b/lib/MooseX/Daemonize.pm index 4ce6fd3..4cb6cb3 100644 --- a/lib/MooseX/Daemonize.pm +++ b/lib/MooseX/Daemonize.pm @@ -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 ); } @@ -112,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); } @@ -150,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 . ')'); } @@ -167,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); } @@ -205,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"); } @@ -255,22 +271,38 @@ $_kill = sub { # $!{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! @@ -283,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 instead. =head1 SYNOPSIS @@ -317,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 @@ -326,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 @@ -341,17 +394,38 @@ The name of our daemon, defaults to C<$package_name =~ s/::/_/>; =item I -The base for our bid, defaults to C +The base for our PID, defaults to C + +=item I + +The directory we chdir to; defaults to C. =item I -The file we store our PID in, defaults to C +The file we store our PID in, defaults to C<$pidbase/$progname.pid> =item I 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 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 + +If true, the process will not clean up zombie processes. +Normally you don't want this. + +=item I + +If true, the objects open filehandles will not be closed when daemonized. +Normally you don't want this. + + =item I If true, the process is the backgrounded daemon process, if false it is the @@ -373,7 +447,7 @@ These are the internal attributes, which are not available through MooseX::Getop =item I -=item I +=item I =back @@ -407,6 +481,8 @@ Literally this is: =item B +=item B + =back @@ -458,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 @@ -486,11 +578,13 @@ L. =head1 SEE ALSO -L, L +L, L, L + +=head1 AUTHORS -=head1 AUTHOR +Chris Prather C<< > -Chris Prather C<< >> +Stevan Little C<< >> =head1 THANKS @@ -501,7 +595,7 @@ Some bug fixes sponsored by Takkle Inc. =head1 LICENCE AND COPYRIGHT -Copyright (c) 2007, Chris Prather C<< >>. All rights +Copyright (c) 2007-2011, Chris Prather C<< >>. Some rights reserved. This module is free software; you can redistribute it and/or