From: Stevan Little Date: Mon, 3 Dec 2007 17:38:13 +0000 (+0000) Subject: okay,.. I think this is ready for release X-Git-Tag: 0_06~11 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=4327fe988c1c6e363f8e8a6eeda373e1950fcd17;p=gitmo%2FMooseX-Daemonize.git okay,.. I think this is ready for release --- diff --git a/Makefile.PL b/Makefile.PL index 7acfffd..79fba0e 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -8,8 +8,9 @@ all_from 'lib/MooseX/Daemonize.pm'; # Specific dependencies build_requires 'Test::More' => 0; -requires 'MooseX::Getopt' => 0; -requires 'Moose' => 0.20; +requires 'Moose' => 0.20; +requires 'MooseX::Getopt' => 0.06; +requires 'MooseX::Types::Path::Class' => 0; no_index 'directory' => 'examples'; diff --git a/lib/MooseX/Daemonize.pm b/lib/MooseX/Daemonize.pm index 35c2afe..c67811a 100644 --- a/lib/MooseX/Daemonize.pm +++ b/lib/MooseX/Daemonize.pm @@ -5,38 +5,39 @@ use MooseX::Types::Path::Class; our $VERSION = 0.05; -with qw[ - MooseX::Daemonize::WithPidFile - MooseX::Getopt -]; +with 'MooseX::Daemonize::WithPidFile', + 'MooseX::Getopt'; has progname => ( - isa => 'Str', - is => 'ro', - lazy => 1, - required => 1, - default => sub { + metaclass => 'Getopt', + isa => 'Str', + is => 'ro', + lazy => 1, + required => 1, + default => sub { ( my $name = lc $_[0]->meta->name ) =~ s/::/_/g; return $name; }, ); has pidbase => ( - isa => 'Path::Class::Dir', - is => 'ro', - coerce => 1, - required => 1, - lazy => 1, - default => sub { Path::Class::Dir->new('var', 'run') }, + metaclass => 'Getopt', + isa => 'Path::Class::Dir', + is => 'ro', + coerce => 1, + required => 1, + lazy => 1, + default => sub { Path::Class::Dir->new('var', 'run') }, ); has basedir => ( - isa => 'Path::Class::Dir', - is => 'ro', - coerce => 1, - required => 1, - lazy => 1, - default => sub { Path::Class::Dir->new('/') }, + metaclass => 'Getopt', + isa => 'Path::Class::Dir', + is => 'ro', + coerce => 1, + required => 1, + lazy => 1, + default => sub { Path::Class::Dir->new('/') }, ); has foreground => ( @@ -48,11 +49,16 @@ has foreground => ( ); has stop_timeout => ( - isa => 'Int', - is => 'rw', - default => sub { 2 } + metaclass => 'Getopt', + isa => 'Int', + is => 'rw', + default => sub { 2 } ); +# methods ... + +## PID file related stuff ... + sub init_pidfile { my $self = shift; my $file = $self->pidbase . '/' . $self->progname . '.pid'; @@ -60,6 +66,25 @@ sub init_pidfile { MooseX::Daemonize::Pid::File->new( file => $file ); } +# backwards compat, +sub check { (shift)->pidfile->is_running } +sub save_pid { (shift)->pidfile->write } +sub remove_pid { (shift)->pidfile->remove } +sub get_pid { (shift)->pidfile->pid } + +## signal handling ... + +sub setup_signals { + my $self = shift; + $SIG{'INT'} = sub { $self->handle_sigint }; + $SIG{'HUP'} = sub { $self->handle_sighup }; +} + +sub handle_sigint { $_[0]->stop; } +sub handle_sighup { $_[0]->restart; } + +## daemon control methods ... + sub start { my ($self) = @_; @@ -69,12 +94,7 @@ sub start { return unless $self->is_daemon; - $self->pidfile->pid($$); - - # Avoid 'stdin reopened for output' - # warning with newer perls - open( NULL, '/dev/null' ); - if (0); + $self->pidfile->pid($$); # Change to basedir chdir $self->basedir; @@ -84,6 +104,12 @@ sub start { return $$; } +sub restart { + my ($self) = @_; + $self->stop( no_exit => 1 ); + $self->start(); +} + # Make _kill *really* private my $_kill; @@ -96,21 +122,6 @@ sub stop { exit; } -sub restart { - my ($self) = @_; - $self->stop( no_exit => 1 ); - $self->start(); -} - -sub setup_signals { - my $self = shift; - $SIG{'INT'} = sub { $self->handle_sigint }; - $SIG{'HUP'} = sub { $self->handle_sighup }; -} - -sub handle_sigint { $_[0]->stop; } -sub handle_sighup { $_[0]->restart; } - $_kill = sub { my ( $self, $pid ) = @_; return unless $pid; @@ -165,103 +176,162 @@ application. =head1 VERSION -This document describes MooseX::Daemonize version 0.04 +This document describes MooseX::Daemonize version 0.05 =head1 SYNOPSIS - package FileMaker; + package My::Daemon; use Moose; + with qw(MooseX::Daemonize); + + # ... define your class .... + + after start => sub { + my $self = shift; + return unless $self->is_daemon; + # your daemon code here ... + }; - sub create_file { - my ( $self, $file ) = @_; - open( FILE, ">$file" ) || die; - close(FILE); - } - - no Moose; - - # then in the main package ... + # then in your script ... + + my $daemon = My::Daemon->new_with_options(); - my $daemon = FileMaker->new(); - $daemon->start(); - $daemon->create_file($file); - $daemon->stop(); + my ($command) = @{$daemon->extra_argv} + defined $command || die "No command specified"; + + $daemon->start() if $command eq 'start'; + $daemon->stop() if $command eq 'stop'; =head1 DESCRIPTION Often you want to write a persistant daemon that has a pid file, and responds -appropriately to Signals. This module helps provide the basic infrastructure -to do that. +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 +exception of C. + =over -=item progname Path::Class::Dir | Str +=item I -The name of our daemon, defaults to $self->meta->name =~ s/::/_/; +The name of our daemon, defaults to C<$package_name =~ s/::/_/>; -=item pidbase Path::Class::Dir | Str +=item I -The base for our bid, defaults to /var/run/$progname +The base for our bid, defaults to C -=item pidfile MooseX::Daemonize::Pid::File | Str +=item I -The file we store our PID in, defaults to /var/run/$progname +The file we store our PID in, defaults to C -=item foreground Bool +=item I If true, the process won't background. Useful for debugging. This option can be set via Getopt's -f. -=item is_daemon Bool +=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 process. This is useful for example -in an after 'start' => sub { } block +B This option is explicitly B available through L. -=item stop_timeout +=item I Number of seconds to wait for the process to stop, before trying harder to kill -it. Defaults to 2 seconds +it. Defaults to 2 seconds. =back =head1 METHODS -=over +=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. -=item start() +Extending these methods is best done with the L method modifiers, +such as C, C and C. + +=over 4 + +=item B Setup a pidfile, fork, then setup the signal handlers. -=item stop() +=item B Stop the process matching the pidfile, and unlinks the pidfile. -=item restart() +=item B -Litterally +Literally this is: $self->stop(); $self->start(); -=item daemonize() +=back + +=head2 Pidfile Handling Methods + +=over 4 + +=item B + +This method will create a L object and tell +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 +the pidfile. -Calls daemonize from MooseX::Daemonize::Core. +=item B -=item setup_signals() +Returns the PID of the daemon process. -Setup the signal handlers, by default it only sets up handlers for SIGINT and SIGHUP +=item B -=item handle_sigint() +Write the pidfile. + +=item B + +Removes the pidfile. + +=back + +=head2 Signal Handling Methods + +=over 4 + +=item B + +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. + +=item B Handle a INT signal, by default calls C<$self->stop()> -=item handle_sighup() +=item B Handle a HUP signal. By default calls C<$self->restart()> +=back + +=head2 Introspection + +=over 4 + =item meta() The C method from L @@ -270,37 +340,14 @@ The C method from L =head1 DEPENDENCIES -=for author to fill in: - A list of all the other modules that this module relies upon, - including any restrictions on versions, and an indication whether - the module is part of the standard Perl distribution, part of the - module's distribution, or must be installed separately. ] - -Obviously L, and L +L, L, L and L =head1 INCOMPATIBILITIES -=for author to fill in: - A list of any modules that this module cannot be used in conjunction - with. This may be due to name conflicts in the interface, or - competition for system or program resources, or due to internal - limitations of Perl (for example, many modules that use source code - filters are mutually incompatible). - -None reported. - +None reported. Although obviously this will not work on Windows. =head1 BUGS AND LIMITATIONS -=for author to fill in: - A list of known problems with the module, together with some - indication Whether they are likely to be fixed in an upcoming - release. Also a list of restrictions on the features the module - does provide: data types that cannot be handled, performance issues - and the circumstances in which they may arise, practical - limitations on the size of data sets, special cases that are not - (yet) handled, etc. - No bugs have been reported. Please report any bugs or feature requests to @@ -309,7 +356,7 @@ L. =head1 SEE ALSO -L, L, L +L, L =head1 AUTHOR @@ -330,7 +377,6 @@ reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. - =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY diff --git a/lib/MooseX/Daemonize/Core.pm b/lib/MooseX/Daemonize/Core.pm index cb1c202..5447f63 100644 --- a/lib/MooseX/Daemonize/Core.pm +++ b/lib/MooseX/Daemonize/Core.pm @@ -1,4 +1,6 @@ package MooseX::Daemonize::Core; +use strict; # cause Perl::Critic errors are annoying +use MooseX::Getopt; # to load the NoGetopt metaclass use Moose::Role; our $VERSION = 0.01; @@ -6,9 +8,14 @@ our $VERSION = 0.01; use POSIX (); has is_daemon => ( - isa => 'Bool', - is => 'rw', - default => sub { 0 }, + # NOTE: + # this should never be accessible + # from the command line + # - SL + metaclass => 'NoGetopt', + isa => 'Bool', + is => 'rw', + default => sub { 0 }, ); sub daemon_fork { @@ -51,23 +58,42 @@ sub daemon_detach { # close them all POSIX::close($_) foreach (0 .. $openmax); - open(STDIN, "+>/dev/null"); + # fixup STDIN ... + + open(STDIN, "+>/dev/null") + or confess "Could not redirect STDOUT to /dev/null"; + + # fixup STDOUT ... if (my $stdout_file = $ENV{MX_DAEMON_STDOUT}) { open STDOUT, ">", $stdout_file or confess "Could not redirect STDOUT to $stdout_file : $!"; } else { - open(STDOUT, "+>&STDIN"); + open(STDOUT, "+>&STDIN") + or confess "Could not redirect STDOUT to /dev/null"; } + # fixup STDERR ... + if (my $stderr_file = $ENV{MX_DAEMON_STDERR}) { open STDERR, ">", "ERR.txt" or confess "Could not redirect STDERR to $stderr_file : $!"; } else { - open(STDERR, "+>&STDIN"); - } + open(STDERR, "+>&STDIN") + or confess "Could not redirect STDERR to /dev/null"; ; + } + + # do a little house cleaning ... + + # Avoid 'stdin reopened for output' + # warning with newer perls + open( NULL, '/dev/null' ); + if (0); + + # return success + return 1; } sub daemonize { @@ -90,9 +116,9 @@ MooseX::Daemonize::Core - A Role with the core daemonization features package My::Daemon; use Moose; - + with 'MooseX::Daemonize::Core'; - + sub start { my $self = shift; # daemonize me ... @@ -104,7 +130,7 @@ MooseX::Daemonize::Core - A Role with the core daemonization features =head1 DESCRIPTION -This is the basic daemonization Role, it provides a few methods (see +This is the basic daemonization Role, it provides a few methods (see below) and the minimum features needed to properly daemonize your code. =head2 Important Notes @@ -113,8 +139,8 @@ None of the methods in this role will exit the parent process for you, it only forks and detaches your child (daemon) process. It is your responsibility to exit the parent process in some way. -There is no PID or PID file management in this role, that is your -responsibility (see some of the other roles in this distro for that). +There is no PID or PID file management in this role, that is your +responsibility (see some of the other roles in this distro for that). =head1 ATTRIBUTES @@ -184,7 +210,7 @@ and MX_DAEMON_STDOUT environment variables. It will look for a filename in either of these variables and redirect STDOUT and/or STDERR to those files. This is useful for debugging and/or testing purposes. --back +=back The C<%options> available for this function are: @@ -225,8 +251,8 @@ The C method from L =item Note about double fork Taken from L -in a comment entitled I, -it is not the definitive statement on the issue, but it's clear and well +in a comment entitled I, +it is not the definitive statement on the issue, but it's clear and well written enough so I decided to reproduce it here. The first fork accomplishes two things - allow the shell to return, @@ -255,8 +281,8 @@ free to specify otherwise using the C<%options>. =item Note about zombies Doing the double fork (see above) tends to get rid of your zombies since -by the time you have double forked your daemon process is then owned by -the init process. However, sometimes the double-fork is more than you +by the time you have double forked your daemon process is then owned by +the init process. However, sometimes the double-fork is more than you really need, and you want to keep your daemon processes a little closer to you. In this case you have to watch out for zombies, you can avoid then by just setting the C option (see above). @@ -265,9 +291,9 @@ by just setting the C option (see above). =head1 ENVIRONMENT VARIABLES -These variables are best just used for debugging and/or testing, but -not used for actual logging. For that, you should reopen STDOUT/ERR on -your own. +These variables are best just used for debugging and/or testing, but +not used for actual logging. For that, you should reopen STDOUT/ERR on +your own. =over 4 diff --git a/lib/MooseX/Daemonize/Pid.pm b/lib/MooseX/Daemonize/Pid.pm index 60b9086..bcfd3b5 100644 --- a/lib/MooseX/Daemonize/Pid.pm +++ b/lib/MooseX/Daemonize/Pid.pm @@ -29,24 +29,37 @@ __END__ =head1 NAME MooseX::Daemonize::Pid - PID management for MooseX::Daemonize - -=head1 SYNOPSIS =head1 DESCRIPTION +This is a very basic Pid management object, it doesn't do all that +much, and mostly just serves as a base class for L. + =head1 ATTRIBUTES -=over +=over 4 -=item pid Int +=item I =back =head1 METHODS -=over +=over 4 + +=item B + +This will clear the value of the I attribute. It is useful for making sure +that the parent process does not have a bad value stored in it. -=item is_running +=item B + +This is a predicate method to tell you if your I attribute has +been initialized yet. + +=item B + +This checks to see if the I is running. =item meta() @@ -82,7 +95,6 @@ reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. - =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY diff --git a/lib/MooseX/Daemonize/Pid/File.pm b/lib/MooseX/Daemonize/Pid/File.pm index 2b3c88a..dcb8697 100644 --- a/lib/MooseX/Daemonize/Pid/File.pm +++ b/lib/MooseX/Daemonize/Pid/File.pm @@ -2,15 +2,31 @@ package MooseX::Daemonize::Pid::File; use strict; # because Kwalitee is pedantic use Moose; use Moose::Util::TypeConstraints; + +our $VERSION = '0.01'; + use MooseX::Types::Path::Class; +use MooseX::Getopt::OptionTypeMap; +# NOTE: +# set up some basic coercions +# that will come in handy +# - SL coerce 'MooseX::Daemonize::Pid::File' => from 'Str' => via { MooseX::Daemonize::Pid::File->new( file => $_ ) } + => from 'ArrayRef' + => via { MooseX::Daemonize::Pid::File->new( file => $_ ) } => from 'Path::Class::File' => via { MooseX::Daemonize::Pid::File->new( file => $_ ) }; - -our $VERSION = '0.01'; + +# NOTE: +# make sure this class plays +# well with MooseX::Getopt +# - SL +MooseX::Getopt::OptionTypeMap->add_option_type_to_map( + 'MooseX::Daemonize::Pid::File' => '=s', +); extends 'MooseX::Daemonize::Pid'; @@ -54,16 +70,29 @@ __END__ =head1 NAME MooseX::Daemonize::Pid::File - PID file management for MooseX::Daemonize - -=head1 SYNOPSIS =head1 DESCRIPTION +This object extends L to add persistence in a Pidfile. + +This class sets up some basic coercion routines for itself so that it can +be created from a I (a file name), I (an array of path components +for a filename) or a I object. + +This class registers it's type with L as well, and is expected +to be passed on the command line as a string (which will then go through the +coercion routines mentioned above). + =head1 ATTRIBUTES =over -=item file Path::Class::File | Str +=item I + +This is inherited from L and extended here to +get it's default value from the Pidfile (if available). + +=item I =back @@ -71,13 +100,29 @@ MooseX::Daemonize::Pid::File - PID file management for MooseX::Daemonize =over -=item remove +=item B + +=item B + +Both of these methods are inherited from L see that +module for more information. + +=item B + +This removes the Pidfile. + +=item B + +This writes the Pidfile. + +=item B -=item write +This checks if the Pidfile exists. -=item does_file_exist +=item B -=item is_running +This checks if the Pidfile exists, if it does it checks to see if the process +is running, if the Pidfile doesn't exist, it returns false. =item meta() diff --git a/lib/MooseX/Daemonize/WithPidFile.pm b/lib/MooseX/Daemonize/WithPidFile.pm index 1e17a13..89741ba 100644 --- a/lib/MooseX/Daemonize/WithPidFile.pm +++ b/lib/MooseX/Daemonize/WithPidFile.pm @@ -1,5 +1,6 @@ package MooseX::Daemonize::WithPidFile; -use strict; +use strict; # cause Perl::Critic errors are annoying +use MooseX::Getopt; # to load the Getopt metaclass use Moose::Role; use MooseX::Daemonize::Pid::File; @@ -11,6 +12,11 @@ with 'MooseX::Daemonize::Core'; requires 'init_pidfile'; has pidfile => ( + # NOTE: + # this should always be accessible + # from the command line IMO + # - SL + metaclass => 'Getopt', isa => 'MooseX::Daemonize::Pid::File', is => 'rw', lazy => 1, @@ -36,4 +42,126 @@ __END__ =pod -=cut \ No newline at end of file +=head1 NAME + +MooseX::Daemonize::WithPidFile - A Role with the core daemonization and pidfile management + +=head1 SYNOPSIS + + package My::Daemon; + use Moose; + + with 'MooseX::Daemonize::WithPidFile'; + + sub start { + my $self = shift; + # daemonize me ... + $self->daemonize; # << this will write the pidfile for you + # return from the parent,... + return unless $self->is_daemon; + # but continue on in the child (daemon) + } + +=head1 DESCRIPTION + +This is a slightly extended basic daemonization Role, it provides +Pidfile management along with the core daemonization features +found in L. + +=head1 ATTRIBUTES + +=over + +=item I rw, isa => MooseX::Daemonize::Pid::File)> + +This attribute holds the L object used +to manage the Pidfile. It will initialize the object using the +C method (which is required by this role). + +=back + +=head1 REQUIRED METHODS + +=over 4 + +=item I + +This method is used to build the I attribute's object. It should +return a L object. + +=item B + +This is a predicate method to tell you if your I attribute has +been initialized yet. + +=back + +=head1 METHODS + +=over 4 + +=item B + +This adds an C method modifier to the C method (from +L) and handles writing your Pidfile for you. + +=item B + +The C method from L + +=back + +=head1 DEPENDENCIES + +L, L and L + +=head1 INCOMPATIBILITIES + +None reported. + +=head1 BUGS AND LIMITATIONS + +No bugs have been reported. + +Please report any bugs or feature requests to +C, or through the web interface at +L. + +=head1 AUTHOR + +Stevan Little C<< >> + +=head1 LICENCE AND COPYRIGHT + +Copyright (c) 2007, Chris Prather C<< >>. All rights +reserved. + +Portions heavily borrowed from L which is copyright Earl Hood. + +This module is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. See L. + +=head1 DISCLAIMER OF WARRANTY + +BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH +YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR, OR CORRECTION. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE +LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, +OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +=cut diff --git a/t/kwalitee.t b/t/kwalitee.t deleted file mode 100644 index 7576615..0000000 --- a/t/kwalitee.t +++ /dev/null @@ -1,5 +0,0 @@ -use Test::More; - -eval { require Test::Kwalitee; Test::Kwalitee->import() }; - -plan( skip_all => 'Test::Kwalitee not installed; skipping' ) if $@; diff --git a/t/perlcritic.t b/t/perlcritic.t deleted file mode 100644 index a136942..0000000 --- a/t/perlcritic.t +++ /dev/null @@ -1,10 +0,0 @@ -#!perl -use Test::More; -eval "require Test::Perl::Critic"; -if ($@) { - Test::More::plan( - skip_all => "Test::Perl::Critic required for testing PBP compliance" - ); -} - -Test::Perl::Critic::all_critic_ok();