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;
+our $VERSION = '0.15';
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 },
);
+has ignore_zombies => (
+ metaclass => 'Getopt',
+ isa => 'Bool',
+ is => 'rw',
+ default => sub { 0 },
+);
+
+has no_double_fork => (
+ metaclass => 'Getopt',
+ isa => 'Bool',
+ is => 'rw',
+ default => sub { 0 },
+);
+
+has dont_close_all_files => (
+ metaclass => 'Getopt',
+ isa => 'Bool',
+ is => 'rw',
+ default => sub { 0 },
+);
+
+sub _get_options {
+ my ($self, %options) = @_;
+ # backwards compability.. old code might be calling daemon_fork/_detach with options
+ foreach my $opt (qw( ignore_zombies no_double_fork dont_close_all_files )) {
+ $self->$opt( $options{ $opt } ) if ( defined $options{ $opt } );
+ }
+}
+
+
sub daemon_fork {
my ($self, %options) = @_;
+ $self->_get_options( %options );
+
$SIG{CHLD} = 'IGNORE'
- if $options{ignore_zombies};
+ if $self->ignore_zombies;;
if (my $pid = fork) {
return $pid;
return unless $self->is_daemon; # return if parent ...
+ $self->_get_options( %options );
# now we are in the daemon ...
(POSIX::setsid) # set session id
|| confess "Cannot detach from controlling process";
- unless ($options{no_double_fork}) {
+ unless ( $self->no_double_fork ) {
$SIG{'HUP'} = 'IGNORE';
fork && exit;
}
chdir '/'; # change to root directory
umask 0; # clear the file creation mask
- # get the max numnber of possible file descriptors
- my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX );
- $openmax = 64 if !defined($openmax) || $openmax < 0;
+ unless ( $self->dont_close_all_files ) {
+ # get the max numnber of possible file descriptors
+ my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX );
+ $openmax = 64 if !defined($openmax) || $openmax < 0;
- # close them all
- POSIX::close($_) foreach (0 .. $openmax);
+ # close them all
+ POSIX::close($_) foreach (0 .. $openmax);
+ }
+
+ # fixup STDIN ...
- open(STDIN, "+>/dev/null");
+ 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"
+ open STDERR, ">", $stderr_file
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
This attribute is used to signal if we are within the
daemon process or not.
+=item I<no_double_fork (is => rw, isa => Bool)>
+
+Setting this attribute to true will cause this method to not perform the
+typical double-fork, which is extra added protection from your process
+accidentally aquiring a controlling terminal. More information can be
+found above, and by Googling "double fork daemonize".
+
+If you the double-fork behavior off, you might want to enable the
+I<ignore_zombies>.
+
+=item I<ignore_zombies (is => rw, isa => Bool)>
+
+Setting this attribute to a true value will result in setting the C<$SIG{CHLD}>
+handler to C<IGNORE>. This tells perl to clean up zombie processes. By
+default, and for the most part you don't I<need> it, only when you turn off
+the double fork behavior (with the I<no_double_fork> attribute)
+do you sometimes want this behavior.
+
+=item I<dont_close_all_files (is => rw, isa => Bool)>
+
+Setting this attribute to true will cause it to skip closing all the
+filehandles. This is useful if you are opening things like sockets
+and such in the pre-fork.
+
=back
=head1 METHODS
=over
-=item B<daemon_fork (%options)>
+=item B<daemon_fork (?%options)>
This forks off the child process to be daemonized. Just as with
the built in fork, it returns the child pid to the parent process,
0 to the child process. It will also set the is_daemon flag
appropriately.
-The C<%options> available for this function are:
-
-=over 4
+The C<%options> argument remains for backwards compatability, but
+it is suggested that you use the attributes listed above instead.
-=item I<ignore_zombies>
-
-Setting this key to a true value will result in setting the C<$SIG{CHLD}>
-handler to C<IGNORE>. This tells perl to clean up zombie processes. By
-default, and for the most part you don't I<need> it, only when you turn off
-the double fork behavior (with the I<no_double_fork> option) in C<daemon_detach>
-do you sometimes want this behavior.
-
-=back
-
-=item B<daemon_detach (%options)>
+=item B<daemon_detach (?%options)>
This detaches the new child process from the terminal by doing
the following things.
+The C<%options> argument remains for backwards compatability, but
+it is suggested that you use the attributes listed above instead.
+
=over 4
=item Becomes a session leader
=item Closes all open file descriptors.
+See the I<dont_close_all_files> attribute for information on how to
+change this part of the process.
+
=item Reopen STDERR, STDOUT & STDIN to /dev/null
This behavior can be controlled slightly though the MX_DAEMON_STDERR
in either of these variables and redirect STDOUT and/or STDERR to those
files. This is useful for debugging and/or testing purposes.
--back
-
-The C<%options> available for this function are:
-
-=over 4
-
-=item I<no_double_fork>
-
-Setting this option to true will cause this method to not perform the
-typical double-fork, which is extra added protection from your process
-accidentally aquiring a controlling terminal. More information can be
-found above, and by Googling "double fork daemonize".
-
-If you the double-fork behavior off, you might want to enable the
-I<ignore_zombies> behavior in the C<daemon_fork> method.
-
-=back
-
B<NOTE>
If called from within the parent process (the is_daemon flag is set to
false), this method will simply return and do nothing.
-=item B<daemonize (%options)>
+=item B<daemonize (?%options)>
+
+This will simply call C<daemon_fork> followed by C<daemon_detach>.
-This will simply call C<daemon_fork> followed by C<daemon_detach>, it will
-pass any C<%options> onto both methods.
+The C<%options> argument remains for backwards compatability, but
+it is suggested that you use the attributes listed above instead.
=item meta()
=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,
terminal.
That said, you don't always want this to be the behavior, so you are
-free to specify otherwise using the C<%options>.
+free to specify otherwise using the I<no_double_fork> attribute.
=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).
+by just setting the I<ignore_zombies> attribute (see above).
=back
=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 LICENCE AND COPYRIGHT
-Copyright (c) 2007, Chris Prather C<< <perigrin@cpan.org> >>. All rights
+Copyright (c) 2007-2011, Chris Prather C<< <perigrin@cpan.org> >>. All rights
reserved.
Portions heavily borrowed from L<Proc::Daemon> which is copyright Earl Hood.