# 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';
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 => (
);
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';
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) = @_;
return unless $self->is_daemon;
- $self->pidfile->pid($$);
-
- # Avoid 'stdin reopened for output'
- # warning with newer perls
- open( NULL, '/dev/null' );
- <NULL> if (0);
+ $self->pidfile->pid($$);
# Change to basedir
chdir $self->basedir;
return $$;
}
+sub restart {
+ my ($self) = @_;
+ $self->stop( no_exit => 1 );
+ $self->start();
+}
+
# Make _kill *really* private
my $_kill;
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;
=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<MooseX::Getopt>'s command line handling, with the
+exception of C<is_daemon>.
+
=over
-=item progname Path::Class::Dir | Str
+=item I<progname Path::Class::Dir | Str>
-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<pidbase Path::Class::Dir | Str>
-The base for our bid, defaults to /var/run/$progname
+The base for our bid, defaults to C</var/run/$progname>
-=item pidfile MooseX::Daemonize::Pid::File | Str
+=item I<pidfile MooseX::Daemonize::Pid::File | Str>
-The file we store our PID in, defaults to /var/run/$progname
+The file we store our PID in, defaults to C</var/run/$progname>
-=item foreground Bool
+=item I<foreground Bool>
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<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 process. This is useful for example
-in an after 'start' => sub { } block
+B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>.
-=item stop_timeout
+=item I<stop_timeout>
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<Moose> method modifiers,
+such as C<before>, C<after> and C<around>.
+
+=over 4
+
+=item B<start>
Setup a pidfile, fork, then setup the signal handlers.
-=item stop()
+=item B<stop>
Stop the process matching the pidfile, and unlinks the pidfile.
-=item restart()
+=item B<restart>
-Litterally
+Literally this is:
$self->stop();
$self->start();
-=item daemonize()
+=back
+
+=head2 Pidfile Handling Methods
+
+=over 4
+
+=item B<init_pidfile>
+
+This method will create a L<MooseX::Daemonize::Pid::File> object and tell
+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
+the pidfile.
-Calls daemonize from MooseX::Daemonize::Core.
+=item B<get_pid>
-=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<save_pid>
-=item handle_sigint()
+Write the pidfile.
+
+=item B<remove_pid>
+
+Removes the pidfile.
+
+=back
+
+=head2 Signal Handling Methods
+
+=over 4
+
+=item B<setup_signals>
+
+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.
+
+=item B<handle_sigint>
Handle a INT signal, by default calls C<$self->stop()>
-=item handle_sighup()
+=item B<handle_sighup>
Handle a HUP signal. By default calls C<$self->restart()>
+=back
+
+=head2 Introspection
+
+=over 4
+
=item meta()
The C<meta()> method from L<Class::MOP::Class>
=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<Moose>, and L<Proc::Daemon>
+L<Moose>, L<MooseX::Getopt>, L<MooseX::Types::Path::Class> and L<POSIX>
=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
=head1 SEE ALSO
-L<Proc::Daemon>, L<Daemon::Generic>, L<MooseX::Getopt>
+L<Proc::Daemon>, L<Daemon::Generic>
=head1 AUTHOR
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.
-
=head1 DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
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;
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 {
# 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' );
+ <NULL> if (0);
+
+ # return success
+ return 1;
}
sub daemonize {
package My::Daemon;
use Moose;
-
+
with 'MooseX::Daemonize::Core';
-
+
sub start {
my $self = shift;
# daemonize me ...
=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
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
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:
=item Note about double fork
Taken from L<http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012>
-in a comment entitled I<The second fork _is_ necessary by Jonathan Bartlett>,
-it is not the definitive statement on the issue, but it's clear and well
+in a comment entitled I<The second fork _is_ necessary by Jonathan Bartlett>,
+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,
=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<ignore_zombies> 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
=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<MooseX::Daemonize::Pid::File>.
+
=head1 ATTRIBUTES
-=over
+=over 4
-=item pid Int
+=item I<pid Int>
=back
=head1 METHODS
-=over
+=over 4
+
+=item B<clear_pid>
+
+This will clear the value of the I<pid> 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<has_pid>
+
+This is a predicate method to tell you if your I<pid> attribute has
+been initialized yet.
+
+=item B<is_running>
+
+This checks to see if the I<pid> is running.
=item meta()
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.
-
=head1 DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
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';
=head1 NAME
MooseX::Daemonize::Pid::File - PID file management for MooseX::Daemonize
-
-=head1 SYNOPSIS
=head1 DESCRIPTION
+This object extends L<MooseX::Daemonize::Pid> 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<Str> (a file name), I<ArrayRef> (an array of path components
+for a filename) or a I<Path::Class::File> object.
+
+This class registers it's type with L<MooseX::Getopt> 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<pid Int>
+
+This is inherited from L<MooseX:Daemonize::Pid> and extended here to
+get it's default value from the Pidfile (if available).
+
+=item I<file Path::Class::File | Str>
=back
=over
-=item remove
+=item B<clear_pid>
+
+=item B<has_pid>
+
+Both of these methods are inherited from L<MooseX:Daemonize::Pid> see that
+module for more information.
+
+=item B<remove>
+
+This removes the Pidfile.
+
+=item B<write>
+
+This writes the Pidfile.
+
+=item B<does_file_exist>
-=item write
+This checks if the Pidfile exists.
-=item does_file_exist
+=item B<is_running>
-=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()
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;
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,
=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<MooseX::Daemonize::Core>.
+
+=head1 ATTRIBUTES
+
+=over
+
+=item I<pidfile (is => rw, isa => MooseX::Daemonize::Pid::File)>
+
+This attribute holds the L<MooseX::Daemonize::Pid::File> object used
+to manage the Pidfile. It will initialize the object using the
+C<init_pidfile> method (which is required by this role).
+
+=back
+
+=head1 REQUIRED METHODS
+
+=over 4
+
+=item I<init_pidfile>
+
+This method is used to build the I<pidfile> attribute's object. It should
+return a L<MooseX::Daemonize::Pid::File> object.
+
+=item B<has_pidfile>
+
+This is a predicate method to tell you if your I<pidfile> attribute has
+been initialized yet.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item B<daemonize>
+
+This adds an C<after> method modifier to the C<daemonize> method (from
+L<MooseX::Daemonize::Core>) and handles writing your Pidfile for you.
+
+=item B<meta>
+
+The C<meta()> method from L<Class::MOP::Class>
+
+=back
+
+=head1 DEPENDENCIES
+
+L<Moose::Role>, L<MooseX::Getopt> and L<MooseX::Daemonize::Pid::File>
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS AND LIMITATIONS
+
+No bugs have been reported.
+
+Please report any bugs or feature requests to
+C<bug-acme-dahut-call@rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org>.
+
+=head1 AUTHOR
+
+Stevan Little C<< <stevan.little@iinteractive.com> >>
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Chris Prather C<< <perigrin@cpan.org> >>. All rights
+reserved.
+
+Portions heavily borrowed from L<Proc::Daemon> 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<perlartistic>.
+
+=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
+++ /dev/null
-use Test::More;
-
-eval { require Test::Kwalitee; Test::Kwalitee->import() };
-
-plan( skip_all => 'Test::Kwalitee not installed; skipping' ) if $@;
+++ /dev/null
-#!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();